Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,44 @@ on:
- 'stl-preview-base/**'

jobs:
build:
timeout-minutes: 10
name: build
permissions:
contents: read
id-token: write
runs-on: ${{ github.repository == 'stainless-sdks/ark-ruby' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: |-
github.repository == 'stainless-sdks/ark-ruby' &&
(github.event_name == 'push' || github.event.pull_request.head.repo.fork)
steps:
- uses: actions/checkout@v6
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: false
- run: |-
bundle install

- name: Get GitHub OIDC Token
if: |-
github.repository == 'stainless-sdks/ark-ruby' &&
!startsWith(github.ref, 'refs/heads/stl/')
id: github-oidc
uses: actions/github-script@v8
with:
script: core.setOutput('github_token', await core.getIDToken());

- name: Build and upload gem artifacts
if: |-
github.repository == 'stainless-sdks/ark-ruby' &&
!startsWith(github.ref, 'refs/heads/stl/')
env:
URL: https://pkg.stainless.com/s
AUTH: ${{ steps.github-oidc.outputs.github_token }}
SHA: ${{ github.sha }}
PACKAGE_NAME: ark_email
run: ./scripts/utils/upload-artifact.sh
lint:
timeout-minutes: 10
name: lint
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.19.0"
".": "0.20.0"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 58
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-ee4b9d190e3aaa146b08bc0ffed1c802dc353c3fdc37fc0097f2350ab3714b70.yml
openapi_spec_hash: 0dad8b2e562ba7ce879425ab92169d85
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-06c3025bf12b191c3906b28173c9b359e24481dd2839dbf3e6dd0b80c1de3fd6.yml
openapi_spec_hash: d8f8fb1f78579997b6381d64cba4e826
config_hash: b70b11b10fc614f91f1c6f028b40780f
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# Changelog

## 0.20.0 (2026-03-07)

Full Changelog: [v0.19.0...v0.20.0](https://github.com/ArkHQ-io/ark-ruby/compare/v0.19.0...v0.20.0)

### Features

* **api:** add tenantId to send ([d443493](https://github.com/ArkHQ-io/ark-ruby/commit/d4434933efda6c1cc2cab76c52319113f67aab91))


### Bug Fixes

* properly mock time in ruby ci tests ([1af3ac3](https://github.com/ArkHQ-io/ark-ruby/commit/1af3ac341b43b64d6e491c72a0a8bda9408625e1))


### Chores

* **ci:** add build step ([07f4b8f](https://github.com/ArkHQ-io/ark-ruby/commit/07f4b8f01d304d412aef6898dc8d003f701e2733))
* **ci:** skip uploading artifacts on stainless-internal branches ([84760ce](https://github.com/ArkHQ-io/ark-ruby/commit/84760cec19c56830abbe97179c421be8ec8d0573))
* **internal:** codegen related update ([59ed473](https://github.com/ArkHQ-io/ark-ruby/commit/59ed473b3a2c74b6baecde451956fb1a24829489))
* **internal:** codegen related update ([52ec8d2](https://github.com/ArkHQ-io/ark-ruby/commit/52ec8d221046d735dd605aab684f17e074478cf9))
* **test:** do not count install time for mock server timeout ([8be7c3f](https://github.com/ArkHQ-io/ark-ruby/commit/8be7c3f8333306f7e3687ade2d770a928abbf270))
* update mock server docs ([e0cbedc](https://github.com/ArkHQ-io/ark-ruby/commit/e0cbedce73ed0f6dbd683cb3dc04b8aa9937a434))

## 0.19.0 (2026-02-07)

Full Changelog: [v0.18.0...v0.19.0](https://github.com/ArkHQ-io/ark-ruby/compare/v0.18.0...v0.19.0)
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ $ bundle exec rake

Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests.

```bash
$ npx prism mock path/to/your/openapi.yml
```sh
$ ./scripts/mock
```

```bash
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ GIT
PATH
remote: .
specs:
ark-email (0.19.0)
ark-email (0.20.0)
cgi
connection_pool

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ To use this gem, install via Bundler by adding the following to your application
<!-- x-release-please-start-version -->

```ruby
gem "ark-email", "~> 0.19.0"
gem "ark-email", "~> 0.20.0"
```

<!-- x-release-please-end -->
Expand Down
79 changes: 79 additions & 0 deletions lib/ark_email/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,97 @@ class Client < ArkEmail::Internal::Transport::BaseClient
# @return [String]
attr_reader :api_key

# Send and manage email messages.
#
# **Quick Reference:**
#
# - `POST /emails` - Send a single email
# - `POST /emails/batch` - Send up to 100 emails
# - `GET /emails/{emailId}` - Get email status and details
# - `GET /emails` - List sent emails
# - `POST /emails/{emailId}/retry` - Retry failed delivery
# @return [ArkEmail::Resources::Emails]
attr_reader :emails

# Access API request logs for debugging and monitoring.
#
# Every API request is logged with details including:
#
# - Request method, path, and endpoint
# - Response status code and duration
# - Error details (code, message) for failed requests
# - SDK information (name, version)
# - Rate limit state at time of request
# - Request and response bodies (for single log retrieval)
#
# **Retention:** Logs are retained for 90 days.
#
# **Body storage:** Request and response bodies are stored encrypted and truncated
# at 25KB. Bodies are only returned when retrieving a single log entry.
#
# **Quick Reference:**
#
# - `GET /logs` - List API request logs with filters
# - `GET /logs/{requestId}` - Get full details including request/response bodies
# @return [ArkEmail::Resources::Logs]
attr_reader :logs

# Per-tenant usage analytics and bulk reporting.
#
# Track email sending statistics for each tenant to power billing, dashboards, and
# monitoring.
#
# **Single Tenant Usage:**
#
# - `GET /tenants/{id}/usage` - Get usage stats for a specific tenant
# - `GET /tenants/{id}/usage/timeseries` - Get time-bucketed data for charts
#
# **Bulk Usage:**
#
# - `GET /usage/tenants` - Get usage for all tenants (paginated, sortable)
# - `GET /usage/export` - Export usage data as CSV, JSONL, or JSON
#
# **Period Formats:**
#
# - Shortcuts: `today`, `yesterday`, `this_month`, `last_month`, `last_7_days`,
# `last_30_days`
# - Month: `2024-01`
# - Date range: `2024-01-01..2024-01-15`
# @return [ArkEmail::Resources::Usage]
attr_reader :usage

# Check account rate limits and send limits.
#
# The limits endpoint returns current status for operational limits:
#
# - **Rate limit:** API requests per second (currently 10/sec)
# - **Send limit:** Emails per hour (default 100/hour for new accounts)
# - **Billing:** Credit balance and auto-recharge configuration
#
# **AI Integration Note:** This endpoint is designed for AI agents and MCP servers
# to understand account constraints before taking actions. Call this endpoint
# first when planning batch operations to avoid hitting limits unexpectedly.
#
# **Quick Reference:**
#
# - `GET /limits` - Get current rate limits and send limits
# - `GET /usage` - (Deprecated) Use `/limits` instead
# @return [ArkEmail::Resources::Limits]
attr_reader :limits

# Manage tenants (your customers).
#
# Create a tenant for each of your customers to track their email sending
# separately. Store the tenant `id` in your database and use `metadata` for any
# custom data.
#
# **Quick Reference:**
#
# - `POST /tenants` - Create a new tenant
# - `GET /tenants` - List all tenants (paginated)
# - `GET /tenants/{id}` - Get tenant details
# - `PATCH /tenants/{id}` - Update tenant name, metadata, or status
# - `DELETE /tenants/{id}` - Delete a tenant
# @return [ArkEmail::Resources::Tenants]
attr_reader :tenants

Expand Down
31 changes: 31 additions & 0 deletions lib/ark_email/internal/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,37 @@ def writable_enum(&blk)
JSONL_CONTENT = %r{^application/(:?x-(?:n|l)djson)|(:?(?:x-)?jsonl)}

class << self
# @api private
#
# @param query [Hash{Symbol=>Object}]
#
# @return [Hash{Symbol=>Object}]
def encode_query_params(query)
out = {}
query.each { write_query_param_element!(out, _1, _2) }
out
end

# @api private
#
# @param collection [Hash{Symbol=>Object}]
# @param key [String]
# @param element [Object]
#
# @return [nil]
private def write_query_param_element!(collection, key, element)
case element
in Hash
element.each do |name, value|
write_query_param_element!(collection, "#{key}[#{name}]", value)
end
in Array
collection[key] = element.map(&:to_s).join(",")
else
collection[key] = element.to_s
end
end

# @api private
#
# @param y [Enumerator::Yielder]
Expand Down
10 changes: 9 additions & 1 deletion lib/ark_email/models/email_list_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ class EmailListResponse < ArkEmail::Internal::Type::BaseModel
# @return [String]
required :subject, String

# @!attribute tenant_id
# The tenant ID this email belongs to
#
# @return [String]
required :tenant_id, String, api_name: :tenantId

# @!attribute timestamp
#
# @return [Float]
Expand All @@ -53,7 +59,7 @@ class EmailListResponse < ArkEmail::Internal::Type::BaseModel
# @return [String, nil]
optional :tag, String

# @!method initialize(id:, from:, status:, subject:, timestamp:, timestamp_iso:, to:, tag: nil)
# @!method initialize(id:, from:, status:, subject:, tenant_id:, timestamp:, timestamp_iso:, to:, tag: nil)
# Some parameter documentations has been truncated, see
# {ArkEmail::Models::EmailListResponse} for more details.
#
Expand All @@ -65,6 +71,8 @@ class EmailListResponse < ArkEmail::Internal::Type::BaseModel
#
# @param subject [String]
#
# @param tenant_id [String] The tenant ID this email belongs to
#
# @param timestamp [Float]
#
# @param timestamp_iso [Time]
Expand Down
8 changes: 7 additions & 1 deletion lib/ark_email/models/email_retrieve_deliveries_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ class EmailRetrieveDeliveriesParams < ArkEmail::Internal::Type::BaseModel
extend ArkEmail::Internal::Type::RequestParameters::Converter
include ArkEmail::Internal::Type::RequestParameters

# @!method initialize(request_options: {})
# @!attribute email_id
#
# @return [String]
required :email_id, String

# @!method initialize(email_id:, request_options: {})
# @param email_id [String]
# @param request_options [ArkEmail::RequestOptions, Hash{Symbol=>Object}]
end
end
Expand Down
10 changes: 9 additions & 1 deletion lib/ark_email/models/email_retrieve_deliveries_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,13 @@ class Data < ArkEmail::Internal::Type::BaseModel
# @return [Symbol, ArkEmail::Models::EmailRetrieveDeliveriesResponse::Data::Status]
required :status, enum: -> { ArkEmail::Models::EmailRetrieveDeliveriesResponse::Data::Status }

# @!method initialize(id:, can_retry_manually:, deliveries:, retry_state:, status:)
# @!attribute tenant_id
# The tenant ID this email belongs to
#
# @return [String]
required :tenant_id, String, api_name: :tenantId

# @!method initialize(id:, can_retry_manually:, deliveries:, retry_state:, status:, tenant_id:)
# Some parameter documentations has been truncated, see
# {ArkEmail::Models::EmailRetrieveDeliveriesResponse::Data} for more details.
#
Expand All @@ -84,6 +90,8 @@ class Data < ArkEmail::Internal::Type::BaseModel
# @param retry_state [ArkEmail::Models::EmailRetrieveDeliveriesResponse::Data::RetryState, nil] Information about the current retry state of a message that is queued for delive
#
# @param status [Symbol, ArkEmail::Models::EmailRetrieveDeliveriesResponse::Data::Status] Current message status (lowercase). Possible values:
#
# @param tenant_id [String] The tenant ID this email belongs to

class Delivery < ArkEmail::Internal::Type::BaseModel
# @!attribute id
Expand Down
9 changes: 8 additions & 1 deletion lib/ark_email/models/email_retrieve_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ class EmailRetrieveParams < ArkEmail::Internal::Type::BaseModel
extend ArkEmail::Internal::Type::RequestParameters::Converter
include ArkEmail::Internal::Type::RequestParameters

# @!attribute email_id
#
# @return [String]
required :email_id, String

# @!attribute expand
# Comma-separated list of fields to include:
#
Expand All @@ -21,10 +26,12 @@ class EmailRetrieveParams < ArkEmail::Internal::Type::BaseModel
# @return [String, nil]
optional :expand, String

# @!method initialize(expand: nil, request_options: {})
# @!method initialize(email_id:, expand: nil, request_options: {})
# Some parameter documentations has been truncated, see
# {ArkEmail::Models::EmailRetrieveParams} for more details.
#
# @param email_id [String]
#
# @param expand [String] Comma-separated list of fields to include:
#
# @param request_options [ArkEmail::RequestOptions, Hash{Symbol=>Object}]
Expand Down
10 changes: 9 additions & 1 deletion lib/ark_email/models/email_retrieve_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ class Data < ArkEmail::Internal::Type::BaseModel
# @return [String]
required :subject, String

# @!attribute tenant_id
# The tenant ID this email belongs to
#
# @return [String]
required :tenant_id, String, api_name: :tenantId

# @!attribute timestamp
# Unix timestamp when the email was sent
#
Expand Down Expand Up @@ -150,7 +156,7 @@ class Data < ArkEmail::Internal::Type::BaseModel
# @return [String, nil]
optional :tag, String

# @!method initialize(id:, from:, scope:, status:, subject:, timestamp:, timestamp_iso:, to:, activity: nil, attachments: nil, deliveries: nil, headers: nil, html_body: nil, message_id: nil, plain_body: nil, raw_message: nil, spam: nil, spam_score: nil, tag: nil)
# @!method initialize(id:, from:, scope:, status:, subject:, tenant_id:, timestamp:, timestamp_iso:, to:, activity: nil, attachments: nil, deliveries: nil, headers: nil, html_body: nil, message_id: nil, plain_body: nil, raw_message: nil, spam: nil, spam_score: nil, tag: nil)
# Some parameter documentations has been truncated, see
# {ArkEmail::Models::EmailRetrieveResponse::Data} for more details.
#
Expand All @@ -164,6 +170,8 @@ class Data < ArkEmail::Internal::Type::BaseModel
#
# @param subject [String] Email subject line
#
# @param tenant_id [String] The tenant ID this email belongs to
#
# @param timestamp [Float] Unix timestamp when the email was sent
#
# @param timestamp_iso [Time] ISO 8601 formatted timestamp
Expand Down
8 changes: 7 additions & 1 deletion lib/ark_email/models/email_retry_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ class EmailRetryParams < ArkEmail::Internal::Type::BaseModel
extend ArkEmail::Internal::Type::RequestParameters::Converter
include ArkEmail::Internal::Type::RequestParameters

# @!method initialize(request_options: {})
# @!attribute email_id
#
# @return [String]
required :email_id, String

# @!method initialize(email_id:, request_options: {})
# @param email_id [String]
# @param request_options [ArkEmail::RequestOptions, Hash{Symbol=>Object}]
end
end
Expand Down
Loading