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,
  "PATCH" => Net::HTTP::Patch,
  "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.



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

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

Instance Method Details

#addListContacts(id, body) ⇒ Object



395
396
397
# File 'lib/mailkite.rb', line 395

def addListContacts(id, body)
  request("POST", "/api/lists/#{id}/contacts", body)
end

#agent(message) ⇒ Object

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



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

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

#checkDomainAvailability(domain) ⇒ Object



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

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

#createBroadcast(body) ⇒ Object



408
409
410
# File 'lib/mailkite.rb', line 408

def createBroadcast(body)
  request("POST", "/api/broadcasts", body)
end

#createDomain(body) ⇒ Object



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

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

#createList(body) ⇒ Object



375
376
377
# File 'lib/mailkite.rb', line 375

def createList(body)
  request("POST", "/api/lists", body)
end

#createRoute(body) ⇒ Object



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

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

#createTemplate(body) ⇒ Object



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

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

#decrypt(envelope, private_key) ⇒ Object



463
464
465
# File 'lib/mailkite.rb', line 463

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

#deleteBroadcast(id) ⇒ Object



420
421
422
# File 'lib/mailkite.rb', line 420

def deleteBroadcast(id)
  request("DELETE", "/api/broadcasts/#{id}")
end

#deleteDomain(id) ⇒ Object



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

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

#deleteList(id) ⇒ Object



387
388
389
# File 'lib/mailkite.rb', line 387

def deleteList(id)
  request("DELETE", "/api/lists/#{id}")
end

#deleteWebhook(id) ⇒ Object



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

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

#encrypt(plaintext, public_key) ⇒ Object



459
460
461
# File 'lib/mailkite.rb', line 459

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

#getBroadcast(id) ⇒ Object



412
413
414
# File 'lib/mailkite.rb', line 412

def getBroadcast(id)
  request("GET", "/api/broadcasts/#{id}")
end

#getDomain(id) ⇒ Object



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

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

#getList(id) ⇒ Object



379
380
381
# File 'lib/mailkite.rb', line 379

def getList(id)
  request("GET", "/api/lists/#{id}")
end

#getMessage(id) ⇒ Object



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

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

#getTemplate(id) ⇒ Object



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

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

#listBaseTemplatesObject



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

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

#listBroadcastsObject

— Broadcasts —————————————————–



404
405
406
# File 'lib/mailkite.rb', line 404

def listBroadcasts
  request("GET", "/api/broadcasts")
end

#listDomainsObject

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



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

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

#listListContacts(id) ⇒ Object



391
392
393
# File 'lib/mailkite.rb', line 391

def listListContacts(id)
  request("GET", "/api/lists/#{id}/contacts")
end

#listListsObject

— Lists ———————————————————-



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

def listLists
  request("GET", "/api/lists")
end

#listMessagesObject

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



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

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

#listRoutesObject

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



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

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

#listTemplatesObject

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



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

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`).



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

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



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

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

#removeListContact(id, contactId) ⇒ Object



399
400
401
# File 'lib/mailkite.rb', line 399

def removeListContact(id, contactId)
  request("DELETE", "/api/lists/#{id}/contacts/#{contactId}")
end

#reply_block_senderObject



455
456
457
# File 'lib/mailkite.rb', line 455

def reply_block_sender
  Mailkite.reply_block_sender
end

#reply_dropObject



451
452
453
# File 'lib/mailkite.rb', line 451

def reply_drop
  Mailkite.reply_drop
end

#reply_okObject

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



443
444
445
# File 'lib/mailkite.rb', line 443

def reply_ok
  Mailkite.reply_ok
end

#reply_spamObject



447
448
449
# File 'lib/mailkite.rb', line 447

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.



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

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.



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

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



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

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.



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

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

#semanticSearch(query) ⇒ Object

— Docs ———————————————————– Semantic search over the MailKite docs. PUBLIC — no auth required. Returns { “query” => …, “matches” => […] }.



431
432
433
# File 'lib/mailkite.rb', line 431

def semanticSearch(query)
  request("GET", "/v1/docs/search?query=#{CGI.escape(query)}")
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.



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

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

#sendBroadcast(id, body) ⇒ Object



424
425
426
# File 'lib/mailkite.rb', line 424

def sendBroadcast(id, body)
  request("POST", "/api/broadcasts/#{id}/send", body)
end

#setWebhook(id, body) ⇒ Object



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

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

#testWebhook(id) ⇒ Object



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

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

#updateBroadcast(id, body) ⇒ Object



416
417
418
# File 'lib/mailkite.rb', line 416

def updateBroadcast(id, body)
  request("PATCH", "/api/broadcasts/#{id}", body)
end

#updateList(id, body) ⇒ Object



383
384
385
# File 'lib/mailkite.rb', line 383

def updateList(id, body)
  request("PATCH", "/api/lists/#{id}", body)
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)


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
276
# File 'lib/mailkite.rb', line 241

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



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

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.



438
439
440
# File 'lib/mailkite.rb', line 438

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