lettermint 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ require 'faraday'
4
4
 
5
5
  module Lettermint
6
6
  class HttpClient
7
- def initialize(api_token:, base_url:, timeout:)
7
+ def initialize(api_token:, base_url:, timeout:, auth_scheme: :project)
8
8
  normalized_url = "#{base_url.chomp('/')}/"
9
9
  @connection = Faraday.new(url: normalized_url) do |f|
10
10
  f.request :json
@@ -14,7 +14,7 @@ module Lettermint
14
14
  f.headers = {
15
15
  'Content-Type' => 'application/json',
16
16
  'Accept' => 'application/json',
17
- 'x-lettermint-token' => api_token,
17
+ **auth_headers(api_token, auth_scheme),
18
18
  'User-Agent' => "Lettermint/#{Lettermint::VERSION} (Ruby; ruby #{RUBY_VERSION})"
19
19
  }
20
20
  end
@@ -57,46 +57,67 @@ module Lettermint
57
57
 
58
58
  private
59
59
 
60
+ def auth_headers(token, scheme)
61
+ case scheme.to_sym
62
+ when :project then { 'x-lettermint-token' => token }
63
+ when :team then { 'Authorization' => "Bearer #{token}" }
64
+ else raise ArgumentError, "Unknown auth_scheme: #{scheme}"
65
+ end
66
+ end
67
+
60
68
  def with_error_handling
61
69
  response = yield
62
70
  handle_response(response)
63
71
  rescue Faraday::TimeoutError, Timeout::Error
64
72
  raise Lettermint::TimeoutError, "Request timeout after #{@connection.options.timeout}s"
73
+ rescue Faraday::SSLError => e
74
+ raise Lettermint::ConnectionError.new(message: "SSL error: #{e.message}", original_exception: e)
65
75
  rescue Faraday::ConnectionFailed => e
66
- raise Lettermint::Error, e.message
76
+ raise Lettermint::ConnectionError.new(message: e.message, original_exception: e)
77
+ rescue Faraday::ParsingError => e
78
+ raise Lettermint::ResponseParsingError.new(message: "API response parsing failed: #{e.message}",
79
+ original_exception: e)
67
80
  end
68
81
 
69
82
  def handle_response(response)
70
83
  return response.body if response.success?
71
84
 
72
- body = response.body.is_a?(Hash) ? response.body : nil
73
- raise_api_error(response.status, body, response.headers)
85
+ raise build_error(response.status, normalize_body(response.body), response.headers)
74
86
  end
75
87
 
76
- def raise_api_error(status, body, headers)
77
- raise build_error(status, body, headers)
88
+ # Hash stays Hash; String is stripped (nil when empty); anything else passes through as parsed.
89
+ def normalize_body(body)
90
+ return body unless body.is_a?(String)
91
+
92
+ stripped = body.strip
93
+ stripped.empty? ? nil : stripped
78
94
  end
79
95
 
80
- def build_error(status, body, headers) # rubocop:disable Metrics
81
- msg = ->(key, fallback) { body&.dig(key) || fallback }
96
+ def build_error(status, body, headers)
97
+ message, code, details = ErrorBody.parse(status, body)
98
+ common = { message: message, response_body: body, code: code, details: details }
82
99
 
83
100
  case status
84
- when 401, 403
85
- AuthenticationError.new(message: msg['message', "HTTP #{status}"],
86
- status_code: status, response_body: body)
87
- when 400
88
- ClientError.new(message: msg['error', 'Unknown client error'], response_body: body)
89
- when 422
90
- ValidationError.new(message: msg['message', 'Validation error'],
91
- error_type: msg['error', 'ValidationError'], response_body: body)
92
- when 429
93
- retry_after = headers && headers['Retry-After'] && Integer(headers['Retry-After'], exception: false)
94
- RateLimitError.new(message: msg['message', 'Rate limit exceeded'],
95
- retry_after: retry_after, response_body: body)
96
- else
97
- HttpRequestError.new(message: msg['message', "HTTP #{status}"],
98
- status_code: status, response_body: body)
101
+ when 401, 403 then AuthenticationError.new(status_code: status, **common)
102
+ when 400 then ClientError.new(**common)
103
+ when 404 then NotFoundError.new(**common)
104
+ when 422 then ValidationError.new(error_type: validation_error_type(body, code), **common)
105
+ when 429 then RateLimitError.new(retry_after: retry_after_from(headers), **common)
106
+ else HttpRequestError.new(status_code: status, **common)
99
107
  end
100
108
  end
109
+
110
+ # A String `error` is kept verbatim; a Hash `error` contributes its code.
111
+ def validation_error_type(body, code)
112
+ error = body.is_a?(Hash) ? body['error'] : nil
113
+ return error if error.is_a?(String)
114
+
115
+ code || 'ValidationError'
116
+ end
117
+
118
+ def retry_after_from(headers)
119
+ value = headers && headers['Retry-After']
120
+ value && Integer(value, exception: false)
121
+ end
101
122
  end
102
123
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lettermint
4
+ module Resources
5
+ # Base class for Team API resources providing shared functionality.
6
+ class Base
7
+ def initialize(http_client:)
8
+ @http_client = http_client
9
+ end
10
+
11
+ private
12
+
13
+ # Builds query parameters for list endpoints.
14
+ # @param page_size [Integer, nil] Number of items per page
15
+ # @param page_cursor [String, nil] Cursor for pagination
16
+ # @param sort [String, nil] Sort field (prefix with - for descending)
17
+ # @param include [String, nil] Related resources to include
18
+ # @param filters [Hash] Filter parameters (converted to filter[key]=value)
19
+ # @return [Hash, nil] Query parameters hash or nil if empty
20
+ def build_params(page_size: nil, page_cursor: nil, sort: nil, include: nil, **filters)
21
+ params = {
22
+ 'page[size]' => page_size,
23
+ 'page[cursor]' => page_cursor,
24
+ 'sort' => sort,
25
+ 'include' => include
26
+ }.compact
27
+
28
+ filters.each { |k, v| params["filter[#{k}]"] = v unless v.nil? }
29
+ params.empty? ? nil : params
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lettermint
4
+ module Resources
5
+ # Domains resource for managing sending domains and DNS verification.
6
+ class Domains < Base
7
+ # List all domains.
8
+ # @param page_size [Integer, nil] Number of items per page (default: 30)
9
+ # @param page_cursor [String, nil] Cursor for pagination
10
+ # @param sort [String, nil] Sort field: domain, created_at, status_changed_at (prefix - for desc)
11
+ # @param status [String, nil] Filter by status (verified, partially_verified, etc.)
12
+ # @param domain [String, nil] Filter by domain (partial match)
13
+ # @return [Hash] Paginated list of domains
14
+ def list(page_size: nil, page_cursor: nil, sort: nil, status: nil, domain: nil)
15
+ params = build_params(page_size:, page_cursor:, sort:, status:, domain:)
16
+ @http_client.get(path: '/domains', params: params)
17
+ end
18
+
19
+ # Create a new domain.
20
+ # @param domain [String] Domain name (max 255 chars)
21
+ # @return [Hash] Created domain data
22
+ def create(domain:)
23
+ @http_client.post(path: '/domains', data: { domain: domain })
24
+ end
25
+
26
+ # Get domain details.
27
+ # @param id [String] Domain ID
28
+ # @param include [String, nil] Related data to include (dnsRecords, dnsRecordsCount, dnsRecordsExists)
29
+ # @return [Hash] Domain data with optional includes
30
+ def find(id, include: nil)
31
+ params = build_params(include: include)
32
+ @http_client.get(path: "/domains/#{id}", params: params)
33
+ end
34
+
35
+ # Delete a domain.
36
+ # @param id [String] Domain ID
37
+ # @return [Hash] Confirmation message
38
+ def delete(id)
39
+ @http_client.delete(path: "/domains/#{id}")
40
+ end
41
+
42
+ # Verify all DNS records for a domain.
43
+ # @param id [String] Domain ID
44
+ # @return [Hash] Verification result
45
+ def verify_dns(id)
46
+ @http_client.post(path: "/domains/#{id}/dns-records/verify")
47
+ end
48
+
49
+ # Verify a specific DNS record.
50
+ # @param domain_id [String] Domain ID
51
+ # @param record_id [String] DNS record ID
52
+ # @return [Hash] Verification result
53
+ def verify_dns_record(domain_id, record_id)
54
+ @http_client.post(path: "/domains/#{domain_id}/dns-records/#{record_id}/verify")
55
+ end
56
+
57
+ # Update projects associated with a domain.
58
+ # @param id [String] Domain ID
59
+ # @param project_ids [Array<String>] Array of project UUIDs
60
+ # @return [Hash] Updated domain data
61
+ def update_projects(id, project_ids:)
62
+ @http_client.put(path: "/domains/#{id}/projects", data: { project_ids: project_ids })
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lettermint
4
+ module Resources
5
+ # Messages resource for viewing sent and received messages.
6
+ class Messages < Base
7
+ # List messages.
8
+ # @param page_size [Integer, nil] Number of items per page (default: 30)
9
+ # @param page_cursor [String, nil] Cursor for pagination
10
+ # @param sort [String, nil] Sort field: type, status, from_email, subject, created_at, status_changed_at
11
+ # @param type [String, nil] Filter: inbound, outbound
12
+ # @param status [String, nil] Filter by status
13
+ # @param route_id [String, nil] Filter by route ID
14
+ # @param domain_id [String, nil] Filter by domain ID
15
+ # @param tag [String, nil] Filter by tag
16
+ # @param from_email [String, nil] Filter by sender email
17
+ # @param subject [String, nil] Filter by subject
18
+ # @param from_date [String, nil] Filter from date (Y-m-d)
19
+ # @param to_date [String, nil] Filter to date (Y-m-d)
20
+ # @return [Hash] Paginated list of messages
21
+ # rubocop:disable-next Metrics/ParameterLists
22
+ def list(page_size: nil, page_cursor: nil, sort: nil, type: nil, status: nil,
23
+ route_id: nil, domain_id: nil, tag: nil, from_email: nil, subject: nil,
24
+ from_date: nil, to_date: nil)
25
+ params = build_params(
26
+ page_size: page_size,
27
+ page_cursor: page_cursor,
28
+ sort: sort,
29
+ type: type,
30
+ status: status,
31
+ route_id: route_id,
32
+ domain_id: domain_id,
33
+ tag: tag,
34
+ from_email: from_email,
35
+ subject: subject,
36
+ from_date: from_date,
37
+ to_date: to_date
38
+ )
39
+ @http_client.get(path: '/messages', params: params)
40
+ end
41
+
42
+ # Get message details.
43
+ # @param id [String] Message ID
44
+ # @return [Hash] Message data
45
+ def find(id)
46
+ @http_client.get(path: "/messages/#{id}")
47
+ end
48
+
49
+ # Get message events (delivery history).
50
+ # @param id [String] Message ID
51
+ # @param sort [String, nil] Sort field: timestamp, event
52
+ # @return [Hash] List of message events
53
+ def events(id, sort: nil)
54
+ params = sort ? { 'sort' => sort } : nil
55
+ @http_client.get(path: "/messages/#{id}/events", params: params)
56
+ end
57
+
58
+ # Get raw message source (RFC822 format).
59
+ # @param id [String] Message ID
60
+ # @return [String] Raw message source (message/rfc822)
61
+ def source(id)
62
+ @http_client.get(path: "/messages/#{id}/source", headers: { 'Accept' => 'message/rfc822' })
63
+ end
64
+
65
+ # Get message HTML body.
66
+ # @param id [String] Message ID
67
+ # @return [String] HTML content (text/html)
68
+ def html(id)
69
+ @http_client.get(path: "/messages/#{id}/html", headers: { 'Accept' => 'text/html' })
70
+ end
71
+
72
+ # Get message plain text body.
73
+ # @param id [String] Message ID
74
+ # @return [String] Plain text content (text/plain)
75
+ def text(id)
76
+ @http_client.get(path: "/messages/#{id}/text", headers: { 'Accept' => 'text/plain' })
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lettermint
4
+ module Resources
5
+ # Projects resource for managing projects, members, and accessing routes.
6
+ class Projects < Base
7
+ # List all projects.
8
+ # @param page_size [Integer, nil] Number of items per page (default: 30)
9
+ # @param page_cursor [String, nil] Cursor for pagination
10
+ # @param sort [String, nil] Sort field: name, created_at (prefix - for desc)
11
+ # @param search [String, nil] Search filter
12
+ # @return [Hash] Paginated list of projects
13
+ def list(page_size: nil, page_cursor: nil, sort: nil, search: nil)
14
+ params = build_params(page_size: page_size, page_cursor: page_cursor, sort: sort, search: search)
15
+ @http_client.get(path: '/projects', params: params)
16
+ end
17
+
18
+ # Create a new project.
19
+ # @param name [String] Project name (max 255 chars)
20
+ # @param smtp_enabled [Boolean, nil] Enable SMTP (default: false)
21
+ # @param initial_routes [String, nil] Initial routes: both, transactional, broadcast (default: both)
22
+ # @return [Hash] Created project data including api_token
23
+ def create(name:, smtp_enabled: nil, initial_routes: nil)
24
+ data = { name: name }
25
+ data[:smtp_enabled] = smtp_enabled unless smtp_enabled.nil?
26
+ data[:initial_routes] = initial_routes if initial_routes
27
+ @http_client.post(path: '/projects', data: data)
28
+ end
29
+
30
+ # Get project details.
31
+ #
32
+ # Note: Returns a Hash, not a resource object. To access routes for a project,
33
+ # use `projects.routes(project_id)` rather than chaining on the find result.
34
+ #
35
+ # @param id [String] Project ID
36
+ # @param include [String, nil] Related data: routes, domains, teamMembers, messageStats (+ Count/Exists variants)
37
+ # @return [Hash] Project data with optional includes
38
+ def find(id, include: nil)
39
+ params = include ? { 'include' => include } : nil
40
+ @http_client.get(path: "/projects/#{id}", params: params)
41
+ end
42
+
43
+ # Update a project.
44
+ # @param id [String] Project ID
45
+ # @param name [String, nil] New project name
46
+ # @param smtp_enabled [Boolean, nil] Enable/disable SMTP
47
+ # @param default_route_id [String, nil] Default route UUID
48
+ # @return [Hash] Updated project data
49
+ def update(id, name: nil, smtp_enabled: nil, default_route_id: nil)
50
+ data = {}
51
+ data[:name] = name if name
52
+ data[:smtp_enabled] = smtp_enabled unless smtp_enabled.nil?
53
+ data[:default_route_id] = default_route_id if default_route_id
54
+ @http_client.put(path: "/projects/#{id}", data: data)
55
+ end
56
+
57
+ # Delete a project.
58
+ # @param id [String] Project ID
59
+ # @return [Hash] Confirmation message
60
+ def delete(id)
61
+ @http_client.delete(path: "/projects/#{id}")
62
+ end
63
+
64
+ # Rotate the project API token.
65
+ # @param id [String] Project ID
66
+ # @return [Hash] Contains new_token
67
+ def rotate_token(id)
68
+ @http_client.post(path: "/projects/#{id}/rotate-token")
69
+ end
70
+
71
+ # Update project members (replace all).
72
+ # @param id [String] Project ID
73
+ # @param team_member_ids [Array<String>] Array of team member IDs
74
+ # @return [Hash] Confirmation
75
+ def update_members(id, team_member_ids:)
76
+ @http_client.put(path: "/projects/#{id}/members", data: { team_member_ids: team_member_ids })
77
+ end
78
+
79
+ # Add a member to the project.
80
+ # @param project_id [String] Project ID
81
+ # @param member_id [String] Team member ID
82
+ # @return [Hash] Confirmation
83
+ def add_member(project_id, member_id)
84
+ @http_client.post(path: "/projects/#{project_id}/members/#{member_id}")
85
+ end
86
+
87
+ # Remove a member from the project.
88
+ # @param project_id [String] Project ID
89
+ # @param member_id [String] Team member ID
90
+ # @return [Hash] Confirmation
91
+ def remove_member(project_id, member_id)
92
+ @http_client.delete(path: "/projects/#{project_id}/members/#{member_id}")
93
+ end
94
+
95
+ # Get a routes accessor scoped to this project.
96
+ # @param project_id [String] Project ID
97
+ # @return [Routes] Routes resource scoped to the project
98
+ def routes(project_id)
99
+ Routes.new(http_client: @http_client, project_id: project_id)
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lettermint
4
+ module Resources
5
+ # Routes resource for managing project routes (transactional, broadcast, inbound).
6
+ # Can be instantiated with a project_id for scoped operations or without for direct route access.
7
+ class Routes < Base
8
+ # @param http_client [HttpClient] HTTP client instance
9
+ # @param project_id [String, nil] Optional project ID for scoped operations
10
+ def initialize(http_client:, project_id: nil)
11
+ super(http_client: http_client)
12
+ @project_id = project_id
13
+ end
14
+
15
+ # List routes for a project.
16
+ # Requires project_id to be set (via constructor or projects.routes(id)).
17
+ # @param page_size [Integer, nil] Number of items per page (default: 30)
18
+ # @param page_cursor [String, nil] Cursor for pagination
19
+ # @param sort [String, nil] Sort field: name, slug, created_at (prefix - for desc)
20
+ # @param route_type [String, nil] Filter: transactional, broadcast, inbound
21
+ # @param is_default [Boolean, nil] Filter by default route status
22
+ # @param search [String, nil] Search filter
23
+ # @return [Hash] Paginated list of routes
24
+ # rubocop:disable-next Metrics/ParameterLists
25
+ def list(page_size: nil, page_cursor: nil, sort: nil, route_type: nil,
26
+ is_default: nil, search: nil)
27
+ raise ArgumentError, 'project_id required for listing routes' unless @project_id
28
+
29
+ params = build_params(
30
+ page_size: page_size,
31
+ page_cursor: page_cursor,
32
+ sort: sort,
33
+ route_type: route_type,
34
+ is_default: is_default,
35
+ search: search
36
+ )
37
+ @http_client.get(path: "/projects/#{@project_id}/routes", params: params)
38
+ end
39
+
40
+ # Create a new route in a project.
41
+ # Requires project_id to be set.
42
+ # @param name [String] Route name (max 255 chars)
43
+ # @param route_type [String] Type: transactional, broadcast, inbound
44
+ # @param slug [String, nil] Optional slug (max 255 chars)
45
+ # @return [Hash] Created route data
46
+ def create(name:, route_type:, slug: nil)
47
+ raise ArgumentError, 'project_id required for creating routes' unless @project_id
48
+
49
+ data = { name: name, route_type: route_type }
50
+ data[:slug] = slug if slug
51
+ @http_client.post(path: "/projects/#{@project_id}/routes", data: data)
52
+ end
53
+
54
+ # Get route details.
55
+ # @param id [String] Route ID
56
+ # @param include [String, nil] Related data: project, statistics
57
+ # @return [Hash] Route data
58
+ def find(id, include: nil)
59
+ params = include ? { 'include' => include } : nil
60
+ @http_client.get(path: "/routes/#{id}", params: params)
61
+ end
62
+
63
+ # Update a route.
64
+ # @param id [String] Route ID
65
+ # @param name [String, nil] New route name
66
+ # @param settings [Hash, nil] Route settings (track_opens, track_clicks, disable_hosted_unsubscribe)
67
+ # @param inbound_settings [Hash, nil] Inbound settings (inbound_domain, spam_threshold, etc.)
68
+ # @return [Hash] Updated route data
69
+ def update(id, name: nil, settings: nil, inbound_settings: nil)
70
+ data = {}
71
+ data[:name] = name if name
72
+ data[:settings] = settings if settings
73
+ data[:inbound_settings] = inbound_settings if inbound_settings
74
+ @http_client.put(path: "/routes/#{id}", data: data)
75
+ end
76
+
77
+ # Delete a route.
78
+ # @param id [String] Route ID
79
+ # @return [Hash] Confirmation message
80
+ def delete(id)
81
+ @http_client.delete(path: "/routes/#{id}")
82
+ end
83
+
84
+ # Verify inbound domain for a route.
85
+ # @param id [String] Route ID
86
+ # @return [Hash] Verification result
87
+ def verify_inbound_domain(id)
88
+ @http_client.post(path: "/routes/#{id}/verify-inbound-domain")
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lettermint
4
+ module Resources
5
+ # Stats resource for retrieving email statistics.
6
+ class Stats < Base
7
+ # Get statistics for a date range.
8
+ # @param from [String] Start date (Y-m-d format, required)
9
+ # @param to [String] End date (Y-m-d format, required, max 90 days from start)
10
+ # @param project_id [String, nil] Filter by project ID
11
+ # @param route_id [String, nil] Filter by a single route ID
12
+ # @param route_ids [Array<String>, String, nil] Filter by multiple route IDs
13
+ # @return [Hash] Stats data with totals and daily breakdown
14
+ def get(from:, to:, project_id: nil, route_id: nil, route_ids: nil)
15
+ params = { 'from' => from, 'to' => to }
16
+ params['project_id'] = project_id if project_id
17
+ params['route_id'] = route_id if route_id
18
+ params['route_ids'] = route_ids if route_ids
19
+ @http_client.get(path: '/stats', params: params)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lettermint
4
+ module Resources
5
+ # Suppressions resource for managing email suppression lists.
6
+ class Suppressions < Base
7
+ # List suppressions.
8
+ # @param page_size [Integer, nil] Number of items per page (default: 30)
9
+ # @param page_cursor [String, nil] Cursor for pagination
10
+ # @param sort [String, nil] Sort field: value, created_at, reason
11
+ # @param scope [String, nil] Filter: team, project, route
12
+ # @param route_id [String, nil] Filter by route ID
13
+ # @param project_id [String, nil] Filter by project ID
14
+ # @param value [String, nil] Filter by suppression value (email/domain/extension)
15
+ # @param reason [String, nil] Filter: spam_complaint, hard_bounce, unsubscribe, manual
16
+ # @return [Hash] Paginated list of suppressions
17
+ # rubocop:disable-next Metrics/ParameterLists
18
+ def list(page_size: nil, page_cursor: nil, sort: nil, scope: nil,
19
+ route_id: nil, project_id: nil, value: nil, reason: nil)
20
+ params = build_params(
21
+ page_size: page_size,
22
+ page_cursor: page_cursor,
23
+ sort: sort,
24
+ scope: scope,
25
+ route_id: route_id,
26
+ project_id: project_id,
27
+ value: value,
28
+ reason: reason
29
+ )
30
+ @http_client.get(path: '/suppressions', params: params)
31
+ end
32
+
33
+ # Create a suppression entry.
34
+ # @param reason [String] Reason: spam_complaint, hard_bounce, unsubscribe, manual
35
+ # @param scope [String] Scope: team, project, route
36
+ # @param email [String, nil] Single email to suppress (max 255 chars)
37
+ # @param emails [Array<String>, nil] Multiple emails to suppress (max 1000)
38
+ # @param route_id [String, nil] Route ID (required if scope is route)
39
+ # @param project_id [String, nil] Project ID (required if scope is project)
40
+ # @return [Hash] Created suppression data
41
+ def create(reason:, scope:, email: nil, emails: nil, route_id: nil, project_id: nil) # rubocop:disable Metrics/ParameterLists
42
+ data = { reason: reason, scope: scope }
43
+ data[:email] = email if email
44
+ data[:emails] = emails if emails
45
+ data[:route_id] = route_id if route_id
46
+ data[:project_id] = project_id if project_id
47
+ @http_client.post(path: '/suppressions', data: data)
48
+ end
49
+
50
+ # Delete a suppression entry.
51
+ # @param id [String] Suppression ID
52
+ # @return [Hash] Confirmation message
53
+ def delete(id)
54
+ @http_client.delete(path: "/suppressions/#{id}")
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lettermint
4
+ module Resources
5
+ # Team resource for managing team settings, usage, and members.
6
+ class Team < Base
7
+ # Get team details.
8
+ # @param include [String, nil] Related data to include (features, featuresCount, featuresExists)
9
+ # @return [Hash] Team data
10
+ def get(include: nil)
11
+ params = include ? { 'include' => include } : nil
12
+ @http_client.get(path: '/team', params: params)
13
+ end
14
+
15
+ # Update team settings.
16
+ # @param name [String] New team name (max 255 chars)
17
+ # @return [Hash] Updated team data
18
+ def update(name:)
19
+ @http_client.put(path: '/team', data: { name: name })
20
+ end
21
+
22
+ # Get team usage statistics.
23
+ # @return [Hash] Current period and up to 12 historical periods
24
+ def usage
25
+ @http_client.get(path: '/team/usage')
26
+ end
27
+
28
+ # List team members.
29
+ # @param page_size [Integer, nil] Number of items per page (default: 30)
30
+ # @param page_cursor [String, nil] Cursor for pagination
31
+ # @return [Hash] Paginated list of team members
32
+ def members(page_size: nil, page_cursor: nil)
33
+ params = build_params(page_size: page_size, page_cursor: page_cursor)
34
+ @http_client.get(path: '/team/members', params: params)
35
+ end
36
+ end
37
+ end
38
+ end