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 = nil, base_url = DEFAULT_BASE_URL, access_token: nil, get_token: nil) ⇒ Client

Authenticate with a Bearer token. api_key (mk_live_…) and the access_token: keyword (an OAuth access token) are equivalent — both are Bearer credentials. For short-lived OAuth tokens pass get_token: — a callable invoked before each request for a fresh token (it wins):

MailKite::Client.new("mk_live_...")
MailKite::Client.new(access_token: my_oauth_token)
MailKite::Client.new(get_token: -> { current_session.access_token })


250
251
252
253
254
# File 'lib/mailkite.rb', line 250

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

Instance Method Details

#addListContacts(id, body) ⇒ Object



609
610
611
# File 'lib/mailkite.rb', line 609

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

#addSuppression(body) ⇒ Object

Suppress an address so this account never sends to it again. body is a hash with "email" (required) and optional "reason" (defaults to manual) and "note".



903
904
905
# File 'lib/mailkite.rb', line 903

def addSuppression(body)
  request("POST", "/api/contacts/suppressions", 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.



401
402
403
# File 'lib/mailkite.rb', line 401

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

#cancelEnrollment(id) ⇒ Object

Cancel one specific run by its enrollment id. To stop whatever is chasing a contact without knowing which run, use stopSequence.



889
890
891
# File 'lib/mailkite.rb', line 889

def cancelEnrollment(id)
  request("DELETE", "/v1/enrollments/#{id}")
end

#checkDomainAvailability(domain) ⇒ Object



484
485
486
# File 'lib/mailkite.rb', line 484

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

#checkSubdomain(name) ⇒ Object

Check whether a free subdomain label can be claimed. Cheap enough to call as the user types; reason is safe to show verbatim.



429
430
431
# File 'lib/mailkite.rb', line 429

def checkSubdomain(name)
  request("GET", "/api/domains/subdomain/check?name=#{CGI.escape(name)}")
end

#claimSubdomain(body) ⇒ Object

Claim a free managed subdomain (<label>.<base>). Comes back already verified with an empty dns array — we host the zone, so there is nothing for the customer to publish.



436
437
438
# File 'lib/mailkite.rb', line 436

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

#createAppPassword(body) ⇒ Object

Create an app password for one domain and address pattern. body is a hash with "domain" (required) plus optional "address" (default "*"), "protocols" (default ["imap"]) and "label". The secret is returned once and never again.



716
717
718
# File 'lib/mailkite.rb', line 716

def createAppPassword(body)
  request("POST", "/api/app-passwords", body)
end

#createBroadcast(body) ⇒ Object



622
623
624
# File 'lib/mailkite.rb', line 622

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

#createDomain(body) ⇒ Object



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

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

#createList(body) ⇒ Object



584
585
586
# File 'lib/mailkite.rb', line 584

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

#createRealtimeTokenObject

--- Realtime ------------------------------------------------------- Mint a short-lived, single-use token that authorises one Realtime API connection. Browsers need this because EventSource cannot set headers; the page passes it as ?token= on GET /v1/realtime. Inherits this credential's scope, expires in five minutes, and burns on first use.



575
576
577
# File 'lib/mailkite.rb', line 575

def createRealtimeToken
  request("POST", "/v1/realtime/token")
end

#createRoute(body) ⇒ Object



497
498
499
# File 'lib/mailkite.rb', line 497

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

#createScopedKey(body) ⇒ Object

Create a key scoped to one domain. body is a hash with "domainId" (required) and optional "name". Ideal for per-site installs (e.g. a WordPress plugin) — the site never holds the account master key.



696
697
698
# File 'lib/mailkite.rb', line 696

def createScopedKey(body)
  request("POST", "/api/keys/scoped", body)
end

#createSequence(sequence) ⇒ Object

Create a sequence: a declared input shape, the steps a contact walks over time, and zero or more triggers. Created as a draft unless you pass status "active". The whole definition is validated up front.



806
807
808
# File 'lib/mailkite.rb', line 806

def createSequence(sequence)
  request("POST", "/v1/sequences", sequence)
end

#createTemplate(body) ⇒ Object



518
519
520
# File 'lib/mailkite.rb', line 518

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

#createTrigger(id, trigger) ⇒ Object

Attach a trigger: when this event arrives, enroll the contact it is about. Never bumps the sequence's version and never touches anyone already in flight. The sequence needs a "from" address first.



852
853
854
# File 'lib/mailkite.rb', line 852

def createTrigger(id, trigger)
  request("POST", "/v1/sequences/#{id}/triggers", trigger)
end

#decrypt(envelope, private_key) ⇒ Object



948
949
950
# File 'lib/mailkite.rb', line 948

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

#deleteAppPassword(id) ⇒ Object

Revoke an app password. Takes effect immediately — any IMAP session or API call using it stops authenticating.



735
736
737
# File 'lib/mailkite.rb', line 735

def deleteAppPassword(id)
  request("DELETE", "/api/app-passwords/#{id}")
end

#deleteBroadcast(id) ⇒ Object



634
635
636
# File 'lib/mailkite.rb', line 634

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

#deleteDomain(id) ⇒ Object



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

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

#deleteList(id) ⇒ Object



596
597
598
# File 'lib/mailkite.rb', line 596

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

#deleteRoute(id) ⇒ Object



501
502
503
# File 'lib/mailkite.rb', line 501

def deleteRoute(id)
  request("DELETE", "/api/routes/#{id}")
end

#deleteScopedKey(id) ⇒ Object

Revoke a domain-scoped key. Takes effect immediately.



701
702
703
# File 'lib/mailkite.rb', line 701

def deleteScopedKey(id)
  request("DELETE", "/api/keys/scoped/#{id}")
end

#deleteSequence(id) ⇒ Object

Delete a sequence and retire every contact still walking it. The response reports how many were canceled.



824
825
826
# File 'lib/mailkite.rb', line 824

def deleteSequence(id)
  request("DELETE", "/v1/sequences/#{id}")
end

#deleteTrackingWebhook(id) ⇒ Object



468
469
470
# File 'lib/mailkite.rb', line 468

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

#deleteTrigger(id) ⇒ Object

Detach a trigger. Stops future enrollments through that door and nothing else.



864
865
866
# File 'lib/mailkite.rb', line 864

def deleteTrigger(id)
  request("DELETE", "/v1/triggers/#{id}")
end

#deleteWebhook(id) ⇒ Object



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

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

#deleteWebhookEvents(id) ⇒ Object



476
477
478
# File 'lib/mailkite.rb', line 476

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

#deliverToRoute(id, body) ⇒ Object

POST stored messages to one webhook route, including mail that arrived before the route existed. body is { messageIds: [...] }, at most 50.



556
557
558
# File 'lib/mailkite.rb', line 556

def deliverToRoute(id, body)
  request("POST", "/api/routes/#{id}/deliver", body)
end

#encrypt(plaintext, public_key) ⇒ Object



944
945
946
# File 'lib/mailkite.rb', line 944

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

#exchangeOauthToken(body) ⇒ Object

Step 3 of linking: exchange the authorization code for an access token, or rotate a refresh token. Finish by calling getApiKey with the access token and storing the key. Public: no API key required.



670
671
672
# File 'lib/mailkite.rb', line 670

def exchangeOauthToken(body)
  request("POST", "/oauth/token", body)
end

#getApiKeyObject

Get the account's unrestricted API key (mk_live_…). Read-or-create: the first call mints it.



676
677
678
# File 'lib/mailkite.rb', line 676

def getApiKey
  request("GET", "/api/keys")
end

#getBroadcast(id) ⇒ Object



626
627
628
# File 'lib/mailkite.rb', line 626

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

#getDomain(id) ⇒ Object



440
441
442
# File 'lib/mailkite.rb', line 440

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

#getEnrollment(id) ⇒ Object

Get one enrollment — which sequence, which step, and what happens next.



877
878
879
# File 'lib/mailkite.rb', line 877

def getEnrollment(id)
  request("GET", "/v1/enrollments/#{id}")
end

#getList(id) ⇒ Object



588
589
590
# File 'lib/mailkite.rb', line 588

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

#getMailboxMessageRaw(uid, address) ⇒ Object

Fetch one message's raw RFC822 bytes from a mailbox. Same app password auth as the list.



749
750
751
752
# File 'lib/mailkite.rb', line 749

def getMailboxMessageRaw(uid, address)
  q = URI.encode_www_form("address" => address)
  request("GET", "/api/mailbox/messages/#{uid}/raw?#{q}")
end

#getMessage(id) ⇒ Object



533
534
535
# File 'lib/mailkite.rb', line 533

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

#getSequence(id) ⇒ Object

Get one sequence with its definition and live enrollment counts.



811
812
813
# File 'lib/mailkite.rb', line 811

def getSequence(id)
  request("GET", "/v1/sequences/#{id}")
end

#getTemplate(id) ⇒ Object



514
515
516
# File 'lib/mailkite.rb', line 514

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

#getUsageObject

Current billing-period usage: emails used vs the plan's included bucket (null = unlimited), AI actions, and the overage state that gates sending. Powers quota meters in dashboards and integrations.



764
765
766
# File 'lib/mailkite.rb', line 764

def getUsage
  request("GET", "/api/billing/usage")
end

#getWebhookSecret(id) ⇒ Object



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

def getWebhookSecret(id)
  request("GET", "/api/domains/#{id}/webhook/secret")
end

#listAppPasswordsObject

List the account's app passwords. Each one opens a mailbox over IMAP and/or the mailbox API, scoped to a domain and an address pattern within it.



708
709
710
# File 'lib/mailkite.rb', line 708

def listAppPasswords
  request("GET", "/api/app-passwords")
end

#listBaseTemplatesObject



510
511
512
# File 'lib/mailkite.rb', line 510

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

#listBroadcastsObject

--- Broadcasts -----------------------------------------------------



618
619
620
# File 'lib/mailkite.rb', line 618

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

#listDeliveryAttempts(id) ⇒ Object

Every captured attempt for one delivery, newest first — the request we sent and the response that came back. Captures are retained for 45 days.



550
551
552
# File 'lib/mailkite.rb', line 550

def listDeliveryAttempts(id)
  request("GET", "/api/deliveries/#{id}/attempts")
end

#listDomainsObject

--- Domains --------------------------------------------------------



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

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

#listEnrollmentRuns(id) ⇒ Object

Every step this enrollment has executed, with the outcome and the reason for it. This is the "why didn't step 3 fire" view.



883
884
885
# File 'lib/mailkite.rb', line 883

def listEnrollmentRuns(id)
  request("GET", "/v1/enrollments/#{id}/runs")
end

#listEnrollments(id, status = nil) ⇒ Object

--- Enrollments ---------------------------------------------------- List who is in a sequence and where each of them is. Filter with status.



870
871
872
873
874
# File 'lib/mailkite.rb', line 870

def listEnrollments(id, status = nil)
  path = "/v1/sequences/#{id}/enrollments"
  path += "?status=#{CGI.escape(status.to_s)}" unless status.nil?
  request("GET", path)
end

#listEventNamesObject

The distinct event names this account works with — both events actually posted and ones your sequences trigger on or wait for but that may never have been sent.



792
793
794
# File 'lib/mailkite.rb', line 792

def listEventNames
  request("GET", "/v1/events/names")
end

#listEvents(name = nil, email = nil) ⇒ Object

List recorded events, newest first — the surface for confirming a POST landed and for debugging a sequence that did not trigger.



780
781
782
783
784
785
786
787
# File 'lib/mailkite.rb', line 780

def listEvents(name = nil, email = nil)
  params = []
  params << "name=#{CGI.escape(name.to_s)}" unless name.nil?
  params << "email=#{CGI.escape(email.to_s)}" unless email.nil?
  path = "/v1/events"
  path += "?#{params.join('&')}" unless params.empty?
  request("GET", path)
end

#listListContacts(id, before = nil, limit = nil) ⇒ Object



600
601
602
603
604
605
606
607
# File 'lib/mailkite.rb', line 600

def listListContacts(id, before = nil, limit = nil)
  params = []
  params << "before=#{CGI.escape(before.to_s)}" unless before.nil?
  params << "limit=#{CGI.escape(limit.to_s)}" unless limit.nil?
  path = "/api/lists/#{id}/contacts"
  path += "?#{params.join('&')}" unless params.empty?
  request("GET", path)
end

#listListsObject

--- Lists ----------------------------------------------------------



580
581
582
# File 'lib/mailkite.rb', line 580

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

#listMailboxMessages(address, params = {}) ⇒ Object

List a mailbox's messages, newest first. Authenticated with an app password granting "api" — this is how an agent reads its own inbox without an IMAP client. params may carry :limit and :before.



742
743
744
745
# File 'lib/mailkite.rb', line 742

def listMailboxMessages(address, params = {})
  q = URI.encode_www_form({ "address" => address }.merge(params))
  request("GET", "/api/mailbox/messages?#{q}")
end

#listMessages(before = nil, limit = nil, search = nil) ⇒ Object

--- Messages & deliveries -----------------------------------------



523
524
525
526
527
528
529
530
531
# File 'lib/mailkite.rb', line 523

def listMessages(before = nil, limit = nil, search = nil)
  params = []
  params << "before=#{CGI.escape(before.to_s)}" unless before.nil?
  params << "limit=#{CGI.escape(limit.to_s)}" unless limit.nil?
  params << "search=#{CGI.escape(search.to_s)}" unless search.nil?
  path = "/api/messages"
  path += "?#{params.join('&')}" unless params.empty?
  request("GET", path)
end

#listRouteCandidates(id, before = nil, limit = nil) ⇒ Object

Stored messages this route could be asked to deliver, newest first.



561
562
563
564
565
566
567
568
# File 'lib/mailkite.rb', line 561

def listRouteCandidates(id, before = nil, limit = nil)
  params = []
  params << "before=#{CGI.escape(before.to_s)}" unless before.nil?
  params << "limit=#{CGI.escape(limit.to_s)}" unless limit.nil?
  path = "/api/routes/#{id}/candidates"
  path += "?#{params.join('&')}" unless params.empty?
  request("GET", path)
end

#listRoutesObject

--- Routes ---------------------------------------------------------



493
494
495
# File 'lib/mailkite.rb', line 493

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

#listScopedKeysObject

List the account's domain-scoped API keys. A scoped key can send and manage only its one domain — hand one to each site or CI job so a leak burns only that surface.



689
690
691
# File 'lib/mailkite.rb', line 689

def listScopedKeys
  request("GET", "/api/keys/scoped")
end

#listSequencesObject

--- Sequences ------------------------------------------------------ List your sequences, newest first, each with live enrollment counts. Archived sequences are omitted.



799
800
801
# File 'lib/mailkite.rb', line 799

def listSequences
  request("GET", "/v1/sequences")
end

#listSuppressionsObject

--- Suppressions ----------------------------------------------------- List suppressed addresses (unsubscribes, hard bounces, spam complaints, manual). Sends to a suppressed address are dropped before delivery.



896
897
898
# File 'lib/mailkite.rb', line 896

def listSuppressions
  request("GET", "/api/contacts/suppressions")
end

#listTemplatesObject

--- Templates ------------------------------------------------------



506
507
508
# File 'lib/mailkite.rb', line 506

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

#listTriggers(id) ⇒ Object

--- Triggers ------------------------------------------------------- List the triggers attached to a sequence — the doors into it.



845
846
847
# File 'lib/mailkite.rb', line 845

def listTriggers(id)
  request("GET", "/v1/sequences/#{id}/triggers")
end

#meObject

The account behind this credential: email, whether it is verified (sending is blocked until it is), and plan. Use to poll verification state after register().



656
657
658
# File 'lib/mailkite.rb', line 656

def me
  request("GET", "/v1/me")
end

#perform(req, uri) ⇒ Object

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



291
292
293
294
295
296
297
298
299
300
301
# File 'lib/mailkite.rb', line 291

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

#register(body) ⇒ Object

--- Account ---------------------------------------------------------- Create a MailKite account from just an email — no password. body is a hash with "email" (required) and optional "channel", "ref" and "referrer". Returns the new account's API key immediately; a verification link is emailed, and SENDING stays blocked until the address is verified (poll me()). An existing email returns 409 account_exists with no credentials. PUBLIC — no API key required.



649
650
651
# File 'lib/mailkite.rb', line 649

def register(body)
  request("POST", "/api/v1/provision", body)
end

#registerDomain(body) ⇒ Object



488
489
490
# File 'lib/mailkite.rb', line 488

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

#registerOauthClient(body) ⇒ Object

Step 1 of linking an EXISTING account: register an OAuth client for this installation (RFC 7591). No pre-shared secret — the client is public and proves itself with PKCE. Public: no API key required.



663
664
665
# File 'lib/mailkite.rb', line 663

def registerOauthClient(body)
  request("POST", "/oauth/register", body)
end

#removeListContact(id, contactId) ⇒ Object



613
614
615
# File 'lib/mailkite.rb', line 613

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

#removeSuppression(email) ⇒ Object

Remove an address from the suppression list. Removing an unsuppressed address is a no-op success.



909
910
911
# File 'lib/mailkite.rb', line 909

def removeSuppression(email)
  request("DELETE", "/api/contacts/suppressions/#{CGI.escape(email)}")
end

#reply_block_senderObject



940
941
942
# File 'lib/mailkite.rb', line 940

def reply_block_sender
  Mailkite.reply_block_sender
end

#reply_dropObject



936
937
938
# File 'lib/mailkite.rb', line 936

def reply_drop
  Mailkite.reply_drop
end

#reply_okObject

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



928
929
930
# File 'lib/mailkite.rb', line 928

def reply_ok
  Mailkite.reply_ok
end

#reply_spamObject



932
933
934
# File 'lib/mailkite.rb', line 932

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.



262
263
264
265
266
267
268
269
270
271
272
# File 'lib/mailkite.rb', line 262

def request(method, path, body = nil)
  uri = URI(@base_url + path)
  req = VERBS.fetch(method).new(uri)
  req["Authorization"] = "Bearer #{token}"
  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.



277
278
279
280
281
282
283
284
285
286
287
# File 'lib/mailkite.rb', line 277

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 #{token}"
  req["Content-Type"] = content_type
  req.body = bytes
  perform(req, uri)
end

#retryDeliveries(body) ⇒ Object

Replay a whole selection of webhook deliveries in one call. body takes deliveryIds / messageIds / threadIds (they combine), at most 50 ids. Always answers 200 with a per-id results array, so branch on that rather than on the status.



544
545
546
# File 'lib/mailkite.rb', line 544

def retryDeliveries(body)
  request("POST", "/api/deliveries/retry", body)
end

#retryDelivery(id) ⇒ Object



537
538
539
# File 'lib/mailkite.rb', line 537

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

#rotateApiKeyObject

Rotate the account API key: the old key stops working immediately and a fresh one is returned. The plaintext is only shown here.



682
683
684
# File 'lib/mailkite.rb', line 682

def rotateApiKey
  request("POST", "/api/keys/rotate")
end

#rotateAppPassword(id) ⇒ Object

Replace an app password's secret, keeping its scope. The old secret stops authenticating immediately; the new one is returned once.



729
730
731
# File 'lib/mailkite.rb', line 729

def rotateAppPassword(id)
  request("POST", "/api/app-passwords/#{id}/rotate")
end

#route(message) ⇒ Object

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



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

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" => [...] }.



916
917
918
# File 'lib/mailkite.rb', line 916

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. Send an email. message is a hash with "from", "to" and a body ("text" and/or "html"). Also accepted: "subject" (optional when a template supplies it), "cc"/"bcc" (string or array), "replyTo", "inReplyTo" (thread a reply), "attachments" (array of url|content, contentType), "headers" (hash of extra raw MIME headers, e.g. List-Unsubscribe — immediate sends only), and "templateId" + "templateData" to render a stored template.



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

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

#sendBatch(batch) ⇒ Object

Send one personalized message per recipient (up to 50) in a single call. batch is a hash with "from" and "recipients" (array of "templateData", "headers" — exactly one address each). Shared fields ("subject", "html"/"text", "templateId", "templateData", "headers", …) form the base message; per-recipient "templateData" and "headers" are merged over the shared ones (the recipient wins). Every message passes the same gates as send() and gets its own id — the response reports each recipient's outcome in order, so a batch can partially succeed.



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

def sendBatch(batch)
  request("POST", "/v1/send/batch", batch)
end

#sendBroadcast(id, body) ⇒ Object



638
639
640
# File 'lib/mailkite.rb', line 638

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

#sendEvent(event) ⇒ Object

--- Events --------------------------------------------------------- Record one application-level fact about a user — user.created, trial.expiring, payment.failed. Every enabled trigger listening for that name enrolls the contact with the payload as its input. Identify the subject with "email" or "contactId", never both; pass a "dedupeKey" to make retries idempotent.



774
775
776
# File 'lib/mailkite.rb', line 774

def sendEvent(event)
  request("POST", "/v1/events", event)
end

#setMailboxMessageFlags(uid, address, flags) ⇒ Object

Replace a message's IMAP flags (e.g. mark it "Seen"). Flags set here are the same ones an IMAP client sees.



756
757
758
759
# File 'lib/mailkite.rb', line 756

def setMailboxMessageFlags(uid, address, flags)
  q = URI.encode_www_form("address" => address)
  request("POST", "/api/mailbox/messages/#{uid}/flags?#{q}", { "flags" => flags })
end

#setTrackingWebhook(id, body) ⇒ Object



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

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

#setWebhook(id, body) ⇒ Object



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

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

#setWebhookEvents(id, body) ⇒ Object



472
473
474
# File 'lib/mailkite.rb', line 472

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

#startSequence(sequence, body = nil) ⇒ Object

Start a sequence for one contact directly — takes a sequence NAME or id, so "start the dunning sequence" needs no lookup. Returns the enrollment it created. Reach for sendEvent instead when your code only knows what HAPPENED and policy should decide what reacts.



832
833
834
# File 'lib/mailkite.rb', line 832

def startSequence(sequence, body = nil)
  request("POST", "/v1/sequences/#{sequence}/start", body)
end

#stopSequence(body) ⇒ Object

Stop whatever is chasing someone — pass the "cancelKey" you set when starting, or "sequence" and "email" together when you did not set one. Always answers 200 with a count, so it is safe to fire blindly.



839
840
841
# File 'lib/mailkite.rb', line 839

def stopSequence(body)
  request("POST", "/v1/sequences/stop", body)
end

#suggestSubdomainObject

Suggest a free, currently-unclaimed subdomain label. Read-only — it does not reserve the name. Read base from the response rather than hard-coding the pool zone.



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

def suggestSubdomain
  request("GET", "/api/domains/subdomain/suggest")
end

#testWebhook(id) ⇒ Object



480
481
482
# File 'lib/mailkite.rb', line 480

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

#tokenObject

The Bearer token for one request: a fresh one from get_token if set, else the static key/token.



257
258
259
# File 'lib/mailkite.rb', line 257

def token
  @get_token ? @get_token.call : @api_key
end

#updateAppPassword(id, body) ⇒ Object

Edit an app password's "label", "address" pattern and "protocols". The domain is fixed for the life of the password: repointing a live credential would hand its holder mail they were never granted.



723
724
725
# File 'lib/mailkite.rb', line 723

def updateAppPassword(id, body)
  request("PATCH", "/api/app-passwords/#{id}", body)
end

#updateBroadcast(id, body) ⇒ Object



630
631
632
# File 'lib/mailkite.rb', line 630

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

#updateList(id, body) ⇒ Object



592
593
594
# File 'lib/mailkite.rb', line 592

def updateList(id, body)
  request("PATCH", "/api/lists/#{id}", body)
end

#updateSequence(id, sequence) ⇒ Object

Edit a sequence. Changing the STEPS bumps its version and contacts already in flight keep walking the version they started on, so an edit can never make someone skip or repeat a step.



818
819
820
# File 'lib/mailkite.rb', line 818

def updateSequence(id, sequence)
  request("PATCH", "/v1/sequences/#{id}", sequence)
end

#updateTrigger(id, trigger) ⇒ Object

Edit a trigger, or toggle "enabled" to switch the door off without deleting it. Everyone already walking the sequence carries on.



858
859
860
# File 'lib/mailkite.rb', line 858

def updateTrigger(id, trigger)
  request("PATCH", "/v1/triggers/#{id}", trigger)
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)


362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/mailkite.rb', line 362

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



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

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.



923
924
925
# File 'lib/mailkite.rb', line 923

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