Class: Mailkite::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/mailkite.rb

Constant Summary collapse

VERBS =
{
  "GET" => Net::HTTP::Get,
  "POST" => Net::HTTP::Post,
  "PUT" => Net::HTTP::Put,
  "DELETE" => Net::HTTP::Delete,
}.freeze
ATTACHMENT_MIME =

Extension → MIME map for raw-binary attachment uploads (default application/octet-stream when an extension is unknown).

{
  "pdf" => "application/pdf",
  "png" => "image/png",
  "jpg" => "image/jpeg",
  "jpeg" => "image/jpeg",
  "gif" => "image/gif",
  "webp" => "image/webp",
  "svg" => "image/svg+xml",
  "csv" => "text/csv",
  "txt" => "text/plain",
  "html" => "text/html",
  "json" => "application/json",
  "zip" => "application/zip",
  "doc" => "application/msword",
  "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  "xls" => "application/vnd.ms-excel",
  "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  "ics" => "text/calendar",
  "ical" => "text/calendar",
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(api_key, base_url = DEFAULT_BASE_URL) ⇒ Client

Returns a new instance of Client.



153
154
155
156
# File 'lib/mailkite.rb', line 153

def initialize(api_key, base_url = DEFAULT_BASE_URL)
  @api_key = api_key
  @base_url = base_url.sub(%r{/+\z}, "")
end

Instance Method Details

#agent(message) ⇒ Object

Run an inbound message through an AI agent. message keys: text (required), plus optional subject/from/html/routeId/address/model.



279
280
281
# File 'lib/mailkite.rb', line 279

def agent(message)
  request("POST", "/v1/agent", message)
end

#checkDomainAvailability(domain) ⇒ Object



322
323
324
# File 'lib/mailkite.rb', line 322

def checkDomainAvailability(domain)
  request("GET", "/api/domains/register/check?domain=#{CGI.escape(domain)}")
end

#createDomain(body) ⇒ Object



294
295
296
# File 'lib/mailkite.rb', line 294

def createDomain(body)
  request("POST", "/api/domains", body)
end

#createRoute(body) ⇒ Object



335
336
337
# File 'lib/mailkite.rb', line 335

def createRoute(body)
  request("POST", "/api/routes", body)
end

#createTemplate(body) ⇒ Object



352
353
354
# File 'lib/mailkite.rb', line 352

def createTemplate(body)
  request("POST", "/api/templates", body)
end

#decrypt(envelope, private_key) ⇒ Object



397
398
399
# File 'lib/mailkite.rb', line 397

def decrypt(envelope, private_key)
  Mailkite.decrypt(envelope, private_key)
end

#deleteDomain(id) ⇒ Object



302
303
304
# File 'lib/mailkite.rb', line 302

def deleteDomain(id)
  request("DELETE", "/api/domains/#{id}")
end

#deleteWebhook(id) ⇒ Object



314
315
316
# File 'lib/mailkite.rb', line 314

def deleteWebhook(id)
  request("DELETE", "/api/domains/#{id}/webhook")
end

#encrypt(plaintext, public_key) ⇒ Object



393
394
395
# File 'lib/mailkite.rb', line 393

def encrypt(plaintext, public_key)
  Mailkite.encrypt(plaintext, public_key)
end

#getDomain(id) ⇒ Object



298
299
300
# File 'lib/mailkite.rb', line 298

def getDomain(id)
  request("GET", "/api/domains/#{id}")
end

#getMessage(id) ⇒ Object



361
362
363
# File 'lib/mailkite.rb', line 361

def getMessage(id)
  request("GET", "/api/messages/#{id}")
end

#getTemplate(id) ⇒ Object



348
349
350
# File 'lib/mailkite.rb', line 348

def getTemplate(id)
  request("GET", "/api/templates/#{id}")
end

#listBaseTemplatesObject



344
345
346
# File 'lib/mailkite.rb', line 344

def listBaseTemplates
  request("GET", "/api/templates/base")
end

#listDomainsObject

— Domains ——————————————————–



290
291
292
# File 'lib/mailkite.rb', line 290

def listDomains
  request("GET", "/api/domains")
end

#listMessagesObject

— Messages & deliveries —————————————–



357
358
359
# File 'lib/mailkite.rb', line 357

def listMessages
  request("GET", "/api/messages")
end

#listRoutesObject

— Routes ———————————————————



331
332
333
# File 'lib/mailkite.rb', line 331

def listRoutes
  request("GET", "/api/routes")
end

#listTemplatesObject

— Templates ——————————————————



340
341
342
# File 'lib/mailkite.rb', line 340

def listTemplates
  request("GET", "/api/templates")
end

#perform(req, uri) ⇒ Object

Send a prepared Net::HTTP request and parse the JSON response (shared by ‘request` and `request_binary`).



188
189
190
191
192
193
194
195
196
197
198
# File 'lib/mailkite.rb', line 188

def perform(req, uri)
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
  text = res.body
  data = text && !text.empty? ? JSON.parse(text) : nil
  code = res.code.to_i
  unless code >= 200 && code < 300
    message = data.is_a?(Hash) ? data["error"] : nil
    raise Error.new(code, message || res.message || "HTTP #{code}", data)
  end
  data
end

#registerDomain(body) ⇒ Object



326
327
328
# File 'lib/mailkite.rb', line 326

def registerDomain(body)
  request("POST", "/api/domains/register", body)
end

#reply_block_senderObject



389
390
391
# File 'lib/mailkite.rb', line 389

def reply_block_sender
  Mailkite.reply_block_sender
end

#reply_dropObject



385
386
387
# File 'lib/mailkite.rb', line 385

def reply_drop
  Mailkite.reply_drop
end

#reply_okObject

Instance wrappers around the module-level crypto helpers. No network call.



377
378
379
# File 'lib/mailkite.rb', line 377

def reply_ok
  Mailkite.reply_ok
end

#reply_spamObject



381
382
383
# File 'lib/mailkite.rb', line 381

def reply_spam
  Mailkite.reply_spam
end

#request(method, path, body = nil) ⇒ Object

Low-level request. Every method below is a one-liner on top of this.



159
160
161
162
163
164
165
166
167
168
169
# File 'lib/mailkite.rb', line 159

def request(method, path, body = nil)
  uri = URI(@base_url + path)
  req = VERBS.fetch(method).new(uri)
  req["Authorization"] = "Bearer #{@api_key}"
  unless body.nil?
    req["Content-Type"] = "application/json"
    req.body = JSON.generate(body)
  end

  perform(req, uri)
end

#request_binary(bytes, filename:, content_type:, retention_days: nil) ⇒ Object

Raw-binary request used by uploadAttachment for ‘bytes`/`path` uploads: the body is the file bytes themselves (not JSON, not multipart) and the Content-Type names the file’s media type. Same auth + response parsing.



174
175
176
177
178
179
180
181
182
183
184
# File 'lib/mailkite.rb', line 174

def request_binary(bytes, filename:, content_type:, retention_days: nil)
  query = { "filename" => filename }
  query["retentionDays"] = retention_days unless retention_days.nil?
  qs = query.map { |k, v| "#{CGI.escape(k)}=#{CGI.escape(v.to_s)}" }.join("&")
  uri = URI("#{@base_url}/v1/attachments?#{qs}")
  req = Net::HTTP::Post.new(uri)
  req["Authorization"] = "Bearer #{@api_key}"
  req["Content-Type"] = content_type
  req.body = bytes
  perform(req, uri)
end

#retryDelivery(id) ⇒ Object



365
366
367
# File 'lib/mailkite.rb', line 365

def retryDelivery(id)
  request("POST", "/api/deliveries/#{id}/retry")
end

#route(message) ⇒ Object

Route an inbound message to its configured destination. message keys: from (required), plus optional routeId/address/subject/text/html.



285
286
287
# File 'lib/mailkite.rb', line 285

def route(message)
  request("POST", "/v1/route", message)
end

#send(message) ⇒ Object

— Sending ——————————————————– message keys: from, to, text/html, etc. ‘subject` is optional when a template supplies it. Pass `templateId` to render a stored template and `templateData` (a hash) to fill its variables.



204
205
206
# File 'lib/mailkite.rb', line 204

def send(message)
  request("POST", "/v1/send", message)
end

#setWebhook(id, body) ⇒ Object



310
311
312
# File 'lib/mailkite.rb', line 310

def setWebhook(id, body)
  request("PUT", "/api/domains/#{id}/webhook", body)
end

#testWebhook(id) ⇒ Object



318
319
320
# File 'lib/mailkite.rb', line 318

def testWebhook(id)
  request("POST", "/api/domains/#{id}/webhook/test")
end

#uploadAttachment(file) ⇒ Object

Upload a file and get back a secure, time-limited URL to reference as a send() attachment ({ filename, url }) or link inline — instead of base64-inlining large files on every send. Provide the file ONE of four ways via the ‘file` hash (priority order):

1. url            — MailKite fetches & re-hosts the remote file
2. bytes          — raw binary string, uploaded directly
3. path           — local file read off disk, then uploaded as bytes
4. content        — base64 string (the original behavior)

Plus optional filename, contentType, and retentionDays.

Raises:

  • (ArgumentError)


240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/mailkite.rb', line 240

def uploadAttachment(file)
  url = file["url"] || file[:url]
  bytes = file["bytes"] || file[:bytes]
  path = file["path"] || file[:path]
  content = file["content"] || file[:content]
  filename = file["filename"] || file[:filename]
  content_type = file["contentType"] || file[:contentType]
  retention_days = file["retentionDays"] || file[:retentionDays]

  if url
    body = { "url" => url }
    body["filename"] = filename unless filename.nil?
    body["contentType"] = content_type unless content_type.nil?
    body["retentionDays"] = retention_days unless retention_days.nil?
    return request("POST", "/v1/attachments", body)
  end

  if bytes || path
    if path
      bytes = File.binread(path)
      filename ||= File.basename(path)
      content_type ||= ATTACHMENT_MIME[File.extname(path).downcase.delete_prefix(".")]
    end
    content_type ||= "application/octet-stream"
    return request_binary(bytes, filename: filename, content_type: content_type, retention_days: retention_days)
  end

  if content
    body = { "content" => content, "filename" => filename }
    body["contentType"] = content_type unless content_type.nil?
    body["retentionDays"] = retention_days unless retention_days.nil?
    return request("POST", "/v1/attachments", body)
  end

  raise ArgumentError, "uploadAttachment requires one of: path, bytes, url, or content"
end

#verifyDomain(id) ⇒ Object



306
307
308
# File 'lib/mailkite.rb', line 306

def verifyDomain(id)
  request("POST", "/api/domains/#{id}/verify")
end

#verifyWebhook(signature, payload, secret, tolerance_ms = DEFAULT_TOLERANCE_MS) ⇒ Object

— Webhooks ——————————————————- Instance wrapper around Mailkite.verify_webhook, so you can verify on an existing client. No network call; no API key required.



372
373
374
# File 'lib/mailkite.rb', line 372

def verifyWebhook(signature, payload, secret, tolerance_ms = DEFAULT_TOLERANCE_MS)
  Mailkite.verify_webhook(signature, payload, secret, tolerance_ms)
end