Glasshouse
루팅된 LG webOS TV용 대시보드·프라이버시 제어·Home Assistant 연동
거래형 이메일 API로 발송하고 웹훅으로 수신하며 맞춤 도메인에 비즈니스 이메일을 호스팅, Mailry로 무료 시작
Send with a transactional email API, receive via inbound webhooks and host business email on your custom domain. Start free with Mailry.
개발 도구TypeScriptGitHub @Mailry온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Mailry SDK Library for JavaScript / TypeScript
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗The official Node.js and TypeScript client for the Mailry.co public API. Send email from your mailboxes, read and manage the messages they hold, upload attachments and look up your domains and email accounts.
fetch, FormData and Blob)Node.js 18 or later. It also runs on Bun, Deno and edge runtimes that provide fetch.
npm install mailry
# or
pnpm add mailry
# or
yarn add mailry
# or
bun add mailry
Create an API key in the Mailry dashboard, then:
import { Mailry } from 'mailry';
const mailry = new Mailry('your_api_key');
const { data: accounts } = await mailry.emailAccounts.list();
await mailry.inbox.send({
emailId: accounts[0].id,
to: '[email protected]',
subject: 'Hello from Mailry',
plainBody: 'Hello!',
htmlBody: '<p>Hello!</p>',
});
CommonJS:
const { Mailry } = require('mailry');
If you leave out the API key, the client reads it from the MAILRY_API_KEY environment variable:
const mailry = new Mailry();
Keep your API key on the server. Never ship it in browser code.
const mailry = new Mailry({
apiKey: 'your_api_key',
baseUrl: 'https://api.mailry.co',
timeout: 60_000,
maxRetries: 2,
headers: { 'X-Request-Source': 'billing-service' },
fetch: customFetch,
});
| Option | Default | Description |
|---|---|---|
apiKey | process.env.MAILRY_API_KEY | Your Mailry API key |
baseUrl | process.env.MAILRY_BASE_URL or https://api.mailry.co | API base URL, for example http://localhost:4000 |
timeout | 60000 | Request timeout in milliseconds |
maxRetries | 2 | Retries for rate limited or transient failures |
headers | {} | Extra headers sent with every request |
fetch | globalThis.fetch | Custom fetch implementation |
Every method also takes an optional last argument to override these per request:
const controller = new AbortController();
await mailry.domains.list({}, { timeout: 5_000, maxRetries: 0, signal: controller.signal });
POST /public/inbox/send
const result = await mailry.inbox.send({
emailId: 'b3f1c2d4-...',
to: ['[email protected]', 'John <[email protected]>'],
subject: 'Weekly report',
plainBody: 'The report is attached.',
htmlBody: '<p>The report is attached.</p>',
});
console.log(result.message);
| Field | Type | Required | Description |
|---|---|---|---|
emailId | string | Yes | ID of the mailbox you send from (see emailAccounts.list()) |
to | string | string[] | Yes | Recipients, up to 50 per request |
cc | string | string[] | No | Carbon copy recipients |
subject | string | Yes | Subject line |
plainBody | string | One of | Plain text body |
htmlBody | string | One of | HTML body |
attachments | Attachment[] | No | Up to 10 files, 5MB each: doc, docx, pdf, xlsx, xls, txt |
At least one of plainBody or htmlBody is required. Each call counts against your daily API send quota.
An attachment is either a File or an object with a filename and content. content can be a Buffer, Uint8Array, ArrayBuffer, Blob or string. The content type is inferred from the file extension unless you set contentType.
import { readFile } from 'node:fs/promises';
await mailry.inbox.send({
emailId,
to: '[email protected]',
subject: 'Your invoice',
htmlBody: '<p>Your invoice is attached.</p>',
attachments: [
{ filename: 'invoice.pdf', content: await readFile('./invoice.pdf') },
{ filename: 'notes.txt', content: 'Thanks for your business!' },
{ filename: 'data.xlsx', content: spreadsheetBuffer, contentType: 'application/vnd.ms-excel' },
],
});
POST /public/inbox/upload-attachment
Upload an office document (doc, docx, xls, xlsx, ppt, pptx, pdf, up to 25MB) ahead of time and get back its ID.
const { data } = await mailry.inbox.uploadAttachment({
filename: 'contract.pdf',
content: await readFile('./contract.pdf'),
});
console.log(data.id, data.fileName, data.fileSize);
GET /public/inbox
Messages come newest first, one row per conversation. With search, which matches subject, sender and body, each matching message is returned on its own.
import { MessageFolder } from 'mailry';
const { data, pagination } = await mailry.inbox.list({
emailId: 'b3f1c2d4-...',
folder: MessageFolder.Inbox,
isStarred: true,
search: 'invoice',
page: 1,
limit: 20,
});
for (const message of data) {
console.log(message.id, message.from, message.subject, message.isReaded);
}
| Field | Type | Description |
|---|---|---|
emailId | string | Only messages of this mailbox |
domainId | string | Only messages of mailboxes on this domain |
folder | MessageFolder | Inbox (1), Sent (2), Draft (3) or Spam (4) |
isStarred | boolean | true returns only starred messages |
search | string | Matches subject, sender and body |
page | number | Page number, starting at 1 |
limit | number | Page size, up to 100 |
GET /public/inbox/{id}
Returns one message with its bodies and attachments, and marks it as read.
const { data: message } = await mailry.inbox.get(messageId);
console.log(message.subject, message.textBody);
for (const attachment of message.attachments ?? []) {
console.log(attachment.originalFileName, attachment.fileUrl);
}
Each attachment's fileUrl is a signed link that expires after a short time. Download it right away rather than storing it.
GET /public/inbox/{id}/thread
Returns every message in the same conversation as the given one, oldest first, and marks them as read.
const { data: messages } = await mailry.inbox.thread(messageId);
PATCH /public/inbox/star
Star or unstar up to 100 messages at a time. Returns how many were updated.
await mailry.inbox.star([firstId, secondId]);
const { data } = await mailry.inbox.star(firstId, false);
console.log(data.updated);
PATCH /public/inbox/spam
Move up to 100 received messages to the Spam folder, or pass false to move them back to the Inbox. Sent and draft messages are left untouched. Returns how many were moved.
await mailry.inbox.markSpam(messageId);
await mailry.inbox.markSpam(messageId, false);
DELETE /public/inbox
Permanently delete messages and their attachments, freeing the storage they used.
await mailry.inbox.delete([firstId, secondId]);
DELETE /public/inbox/attachment/{id}
Delete an attachment you uploaded and no longer need, or one on a stored message.
const { data: uploaded } = await mailry.inbox.uploadAttachment(file);
await mailry.inbox.deleteAttachment(uploaded.id);
GET /public/email
const { data, pagination } = await mailry.emailAccounts.list({
domainId: 'a1b2c3d4-...',
search: 'support',
page: 1,
limit: 20,
});
for (const account of data) {
console.log(account.id, account.email, account.firstName, account.lastName);
}
GET /public/domain
import { DomainStatus } from 'mailry';
const { data } = await mailry.domains.list({ search: 'acme' });
const ready = data.filter((domain) => domain.status === DomainStatus.Active);
List methods return one page along with pagination (currentPage, totalPage, totalData). Page size is capped at 100 by the API.
To walk through every page, use listAll(), which fetches pages as you iterate:
for await (const domain of mailry.domains.listAll()) {
console.log(domain.domainName);
}
for await (const account of mailry.emailAccounts.listAll({ domainId })) {
console.log(account.email);
}
for await (const message of mailry.inbox.listAll({ folder: MessageFolder.Inbox })) {
console.log(message.subject);
}
Every error thrown by the SDK extends MailryError, which has status, body and headers. Errors returned by the API also have details, a list of every validation message.
import {
Mailry,
MailryError,
AuthenticationError,
BadRequestError,
RateLimitError,
} from 'mailry';
try {
await mailry.inbox.send({ emailId, to, subject, plainBody });
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Check your API key');
} else if (error instanceof BadRequestError) {
console.error('Invalid request:', error.details);
} else if (error instanceof RateLimitError) {
console.error(`Slow down, retry in ${error.retryAfter}ms`);
} else if (error instanceof MailryError) {
console.error(error.status, error.message);
} else {
throw error;
}
}
| Error class | When |
|---|---|
BadRequestError | 400 or 422: invalid input, quota exceeded |
AuthenticationError | 401: missing or invalid API key |
PermissionDeniedError | 403: the key lacks permission |
NotFoundError | 404 |
PayloadTooLargeError | 413: attachment too large |
RateLimitError | 429: over 120 requests per minute |
InternalServerError | 5xx |
ConnectionError | The API could not be reached |
TimeoutError | The request exceeded timeout |
MailryError | Base class, also used for invalid SDK arguments |
The API allows 120 requests per minute per API key. Rate limited requests (429) are retried automatically, honouring the Retry-After header. Read requests (GET) are also retried on network errors, timeouts and 5xx responses. Sends are never retried after they reach the server, so an email is not delivered twice. Set maxRetries: 0 to turn retries off.
All request and response types are exported:
import type {
SendEmailParams,
SendEmailResponse,
Message,
MessageAttachment,
ListMessagesParams,
EmailAccount,
Domain,
MailryListResponse,
Attachment,
} from 'mailry';
pnpm install
pnpm test
pnpm typecheck
pnpm build
See the full API reference at api.mailry.co/docs/public.