Class: Mailkite::Client
- Inherits:
-
Object
- Object
- Mailkite::Client
- 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
- #addListContacts(id, body) ⇒ Object
-
#addSuppression(body) ⇒ Object
Suppress an address so this account never sends to it again.
-
#agent(message) ⇒ Object
Run an inbound message through an AI agent.
-
#cancelEnrollment(id) ⇒ Object
Cancel one specific run by its enrollment id.
- #checkDomainAvailability(domain) ⇒ Object
-
#checkSubdomain(name) ⇒ Object
Check whether a free subdomain label can be claimed.
-
#claimSubdomain(body) ⇒ Object
Claim a free managed subdomain (
<label>.<base>). -
#createAppPassword(body) ⇒ Object
Create an app password for one domain and address pattern.
- #createBroadcast(body) ⇒ Object
- #createDomain(body) ⇒ Object
- #createList(body) ⇒ Object
-
#createRealtimeToken ⇒ Object
--- Realtime ------------------------------------------------------- Mint a short-lived, single-use token that authorises one Realtime API connection.
- #createRoute(body) ⇒ Object
-
#createScopedKey(body) ⇒ Object
Create a key scoped to one domain.
-
#createSequence(sequence) ⇒ Object
Create a sequence: a declared input shape, the steps a contact walks over time, and zero or more triggers.
- #createTemplate(body) ⇒ Object
-
#createTrigger(id, trigger) ⇒ Object
Attach a trigger: when this event arrives, enroll the contact it is about.
- #decrypt(envelope, private_key) ⇒ Object
-
#deleteAppPassword(id) ⇒ Object
Revoke an app password.
- #deleteBroadcast(id) ⇒ Object
- #deleteDomain(id) ⇒ Object
- #deleteList(id) ⇒ Object
- #deleteRoute(id) ⇒ Object
-
#deleteScopedKey(id) ⇒ Object
Revoke a domain-scoped key.
-
#deleteSequence(id) ⇒ Object
Delete a sequence and retire every contact still walking it.
- #deleteTrackingWebhook(id) ⇒ Object
-
#deleteTrigger(id) ⇒ Object
Detach a trigger.
- #deleteWebhook(id) ⇒ Object
- #deleteWebhookEvents(id) ⇒ Object
-
#deliverToRoute(id, body) ⇒ Object
POST stored messages to one webhook route, including mail that arrived before the route existed.
- #encrypt(plaintext, public_key) ⇒ Object
-
#exchangeOauthToken(body) ⇒ Object
Step 3 of linking: exchange the authorization code for an access token, or rotate a refresh token.
-
#getApiKey ⇒ Object
Get the account's unrestricted API key (mk_live_…).
- #getBroadcast(id) ⇒ Object
- #getDomain(id) ⇒ Object
-
#getEnrollment(id) ⇒ Object
Get one enrollment — which sequence, which step, and what happens next.
- #getList(id) ⇒ Object
-
#getMailboxMessageRaw(uid, address) ⇒ Object
Fetch one message's raw RFC822 bytes from a mailbox.
- #getMessage(id) ⇒ Object
-
#getSequence(id) ⇒ Object
Get one sequence with its definition and live enrollment counts.
- #getTemplate(id) ⇒ Object
-
#getUsage ⇒ Object
Current billing-period usage: emails used vs the plan's included bucket (null = unlimited), AI actions, and the overage state that gates sending.
- #getWebhookSecret(id) ⇒ Object
-
#initialize(api_key = nil, base_url = DEFAULT_BASE_URL, access_token: nil, get_token: nil) ⇒ Client
constructor
Authenticate with a Bearer token.
-
#listAppPasswords ⇒ Object
List the account's app passwords.
- #listBaseTemplates ⇒ Object
-
#listBroadcasts ⇒ Object
--- Broadcasts -----------------------------------------------------.
-
#listDeliveryAttempts(id) ⇒ Object
Every captured attempt for one delivery, newest first — the request we sent and the response that came back.
-
#listDomains ⇒ Object
--- Domains --------------------------------------------------------.
-
#listEnrollmentRuns(id) ⇒ Object
Every step this enrollment has executed, with the outcome and the reason for it.
-
#listEnrollments(id, status = nil) ⇒ Object
--- Enrollments ---------------------------------------------------- List who is in a sequence and where each of them is.
-
#listEventNames ⇒ Object
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.
-
#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.
- #listListContacts(id, before = nil, limit = nil) ⇒ Object
-
#listLists ⇒ Object
--- Lists ----------------------------------------------------------.
-
#listMailboxMessages(address, params = {}) ⇒ Object
List a mailbox's messages, newest first.
-
#listMessages(before = nil, limit = nil, search = nil) ⇒ Object
--- Messages & deliveries -----------------------------------------.
-
#listRouteCandidates(id, before = nil, limit = nil) ⇒ Object
Stored messages this route could be asked to deliver, newest first.
-
#listRoutes ⇒ Object
--- Routes ---------------------------------------------------------.
-
#listScopedKeys ⇒ Object
List the account's domain-scoped API keys.
-
#listSequences ⇒ Object
--- Sequences ------------------------------------------------------ List your sequences, newest first, each with live enrollment counts.
-
#listSuppressions ⇒ Object
--- Suppressions ----------------------------------------------------- List suppressed addresses (unsubscribes, hard bounces, spam complaints, manual).
-
#listTemplates ⇒ Object
--- Templates ------------------------------------------------------.
-
#listTriggers(id) ⇒ Object
--- Triggers ------------------------------------------------------- List the triggers attached to a sequence — the doors into it.
-
#me ⇒ Object
The account behind this credential: email, whether it is verified (sending is blocked until it is), and plan.
-
#perform(req, uri) ⇒ Object
Send a prepared Net::HTTP request and parse the JSON response (shared by
requestandrequest_binary). -
#register(body) ⇒ Object
--- Account ---------------------------------------------------------- Create a MailKite account from just an email — no password.
- #registerDomain(body) ⇒ Object
-
#registerOauthClient(body) ⇒ Object
Step 1 of linking an EXISTING account: register an OAuth client for this installation (RFC 7591).
- #removeListContact(id, contactId) ⇒ Object
-
#removeSuppression(email) ⇒ Object
Remove an address from the suppression list.
- #reply_block_sender ⇒ Object
- #reply_drop ⇒ Object
-
#reply_ok ⇒ Object
Instance wrappers around the module-level crypto helpers.
- #reply_spam ⇒ Object
-
#request(method, path, body = nil) ⇒ Object
Low-level request.
-
#request_binary(bytes, filename:, content_type:, retention_days: nil) ⇒ Object
Raw-binary request used by uploadAttachment for
bytes/pathuploads: the body is the file bytes themselves (not JSON, not multipart) and the Content-Type names the file's media type. -
#retryDeliveries(body) ⇒ Object
Replay a whole selection of webhook deliveries in one call.
- #retryDelivery(id) ⇒ Object
-
#rotateApiKey ⇒ Object
Rotate the account API key: the old key stops working immediately and a fresh one is returned.
-
#rotateAppPassword(id) ⇒ Object
Replace an app password's secret, keeping its scope.
-
#route(message) ⇒ Object
Route an inbound message to its configured destination.
-
#semanticSearch(query) ⇒ Object
--- Docs ----------------------------------------------------------- Semantic search over the MailKite docs.
-
#send(message) ⇒ Object
--- Sending -------------------------------------------------------- message keys: from, to, text/html, etc.
-
#sendBatch(batch) ⇒ Object
Send one personalized message per recipient (up to 50) in a single call.
- #sendBroadcast(id, body) ⇒ Object
-
#sendEvent(event) ⇒ Object
--- Events --------------------------------------------------------- Record one application-level fact about a user —
user.created,trial.expiring,payment.failed. -
#setMailboxMessageFlags(uid, address, flags) ⇒ Object
Replace a message's IMAP flags (e.g. mark it "Seen").
- #setTrackingWebhook(id, body) ⇒ Object
- #setWebhook(id, body) ⇒ Object
- #setWebhookEvents(id, body) ⇒ Object
-
#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.
-
#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.
-
#suggestSubdomain ⇒ Object
Suggest a free, currently-unclaimed subdomain label.
- #testWebhook(id) ⇒ Object
-
#token ⇒ Object
The Bearer token for one request: a fresh one from get_token if set, else the static key/token.
-
#updateAppPassword(id, body) ⇒ Object
Edit an app password's "label", "address" pattern and "protocols".
- #updateBroadcast(id, body) ⇒ Object
- #updateList(id, body) ⇒ Object
-
#updateSequence(id, sequence) ⇒ Object
Edit a sequence.
-
#updateTrigger(id, trigger) ⇒ Object
Edit a trigger, or toggle "enabled" to switch the door off without deleting it.
-
#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.
- #verifyDomain(id) ⇒ Object
-
#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.
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() request("POST", "/v1/agent", ) 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 |
#createRealtimeToken ⇒ Object
--- 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 |
#getApiKey ⇒ Object
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 |
#getUsage ⇒ Object
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 |
#listAppPasswords ⇒ Object
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 |
#listBaseTemplates ⇒ Object
510 511 512 |
# File 'lib/mailkite.rb', line 510 def listBaseTemplates request("GET", "/api/templates/base") end |
#listBroadcasts ⇒ Object
--- 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 |
#listDomains ⇒ Object
--- 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 |
#listEventNames ⇒ Object
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 |
#listLists ⇒ Object
--- 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 |
#listRoutes ⇒ Object
--- Routes ---------------------------------------------------------
493 494 495 |
# File 'lib/mailkite.rb', line 493 def listRoutes request("GET", "/api/routes") end |
#listScopedKeys ⇒ Object
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 |
#listSequences ⇒ Object
--- 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 |
#listSuppressions ⇒ Object
--- 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 |
#listTemplates ⇒ Object
--- 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 |
#me ⇒ Object
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 = data.is_a?(Hash) ? data["error"] : nil raise Error.new(code, || res. || "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_sender ⇒ Object
940 941 942 |
# File 'lib/mailkite.rb', line 940 def reply_block_sender Mailkite.reply_block_sender end |
#reply_drop ⇒ Object
936 937 938 |
# File 'lib/mailkite.rb', line 936 def reply_drop Mailkite.reply_drop end |
#reply_ok ⇒ Object
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_spam ⇒ Object
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 |
#rotateApiKey ⇒ Object
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() request("POST", "/v1/route", ) 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() request("POST", "/v1/send", ) 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 |
#suggestSubdomain ⇒ Object
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 |
#token ⇒ Object
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.
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 |