Appearance
staff.mail
10 methods on staff.mail. Each returns the answer's data and throws NoviqaError on a failed answer.
list
GET
/admin/v1/mailList the own mailbox (staff)
One page of the caller's own folder: inbox (default), sent or draft. Inbox and sent come one row per thread, newest thread first: the row is the thread's newest message, thread_count is how many messages of the thread the folder holds and read is false while any of them is unread; drafts come one row each, newest first. total counts threads (drafts: drafts), unread counts unread messages. page is 1-based; limit defaults to 50, values outside 1-200 fall back to 50.
More details
With thread (a thread_id) the answer is instead a plain array of every inbox and sent message of that thread, oldest first, and all of them are marked read; folder, page and limit are ignored.
A trader account whose group has mail switched off gets an empty page with enabled false (an empty array for thread); staff always have a mailbox.
Rights: the staff member needs every one of: mailbox.
Parameters
folderstring · querythreadstring · queryone thread across inbox and sent, oldest first
pageinteger · querylimitinteger · queryReturns
Promise<Page>The answer's
data.Errors
400
invalid_input: invalid input401
auth.session_expired: session expired, sign in again403
auth.invalid_terminal: this terminal cannot be used here403
auth.otp_enrol_required: your group requires one-time codes: set them up to continue403
auth.password_change_required: you must change your password first403
forbidden: you do not have access to this action500
internal: something went wrongts
import { staff } from "@/lib/noviqa"
const data = await staff.mail.list({
folder: "string",
thread: "string",
page: 1
})bash
curl -X GET "$NOVIQA_API_URL/admin/v1/mail?folder=string&thread=string&page=1" \
-H "Authorization: Bearer $NOVIQA_TOKEN"json
{
"success": true,
"code": "ok",
"message": "done",
"data": {
"enabled": true,
"items": [
{
"attachments": [
{}
],
"body": "string",
"created_at": "2026-09-25T10:15:00Z",
"draft": {},
"folder": "string",
"from": 1,
"from_name": "string",
"id": 1,
"message_id": "3f2a9c1e-8b7d-4e6f-a5c4-1b2d3e4f5a6b",
"read": true,
"recipients": "string",
"subject": "string"
}
],
"page": 1,
"total": 1,
"unread": 1
},
"request_id": "5b7f2c1e"
}ts
staff.mail.list(query?: { folder?, thread?, page?, limit? }, options?: RequestOptions): Promise<mail.Page>create
POST
/admin/v1/mailSend mail to accounts
Delivers one message to trading accounts in the caller's group scope: either to_logins (1-1000 logins; one outside the scope is refused with forbidden, an unknown one with invalid_input) or to_group (a group mask up to 128 characters, * = every group in scope; reaches every account of the matching groups, at most 100 000) — not both. subject is required (1-256 characters, one line), body is plain text up to 64 KiB; both may use {{login}}, {{name}}, {{group}}, {{currency}} and the company placeholders, filled per recipient. Attachments: up to 5 files per message, each 1 byte to 1 MB, 2 MB together (files already uploaded onto the draft count too); the name must be a plain file name of up to 128 characters (no path, quotes or line breaks) ending in .png, .jpg, .jpeg, .bmp, .gif, .zip, .7z, .doc, .docx, .xls, .xlsx, .odt, .rtf, .csv, .txt, .log or .pdf. In JSON bodies content is the file base64-encoded; the whole request may be 4 MB at most. Accounts whose group has mail switched off are skipped (counted in skipped); if nobody is left the send is refused. email true also queues an e-mail per recipient with a valid address through the mail server named by server_id (0 or left out = the enabled default server, refused if there is none; a named server must exist and be enabled); they appear in GET /admin/v1/mail-outbox. reply_to (a mail of the caller's) joins its thread; draft_id sends from that draft: its uploaded files go along and the draft is removed.
More details
Writes the sender's sent copy and one inbox copy per account, pushes a mail message to each recipient's live sockets and writes a journal line. Not idempotent: a repeat sends again.
Rights: the staff member needs every one of: mailbox.
Request body
attachmentsNewAttachment_input[]bodystringdraft_idintegerthe draft this is sent from; it is removed
emailbooleanalso send it by e-mail through the default mail server
reply_tointegera mail of the sender's this answers (0 = a new thread)
server_idintegerwhich mail server; 0 = the default one
subjectstringto_groupstringto_loginsinteger[]Returns
Promise<Sent>The answer's
data.Errors
400
invalid_input: invalid input401
auth.session_expired: session expired, sign in again403
auth.csrf_invalid: request refused: reload the page and try again403
auth.invalid_terminal: this terminal cannot be used here403
auth.otp_enrol_required: your group requires one-time codes: set them up to continue403
auth.password_change_required: you must change your password first403
forbidden: you do not have access to this action404
not_found: not found500
internal: something went wrongts
import { staff } from "@/lib/noviqa"
const data = await staff.mail.create({
attachments: [
{
content: "JVBERi0xLjQK…",
name: "hours.pdf"
}
],
body: "Dear {{name}}, markets close early on 24 December.",
email: true,
subject: "Holiday trading hours",
to_group: "real\\*"
})bash
curl -X POST "$NOVIQA_API_URL/admin/v1/mail" \
-H "Authorization: Bearer $NOVIQA_TOKEN" \
-H "Content-Type: application/json" \
--data-raw '{
"attachments": [
{
"content": "JVBERi0xLjQK…",
"name": "hours.pdf"
}
],
"body": "Dear {{name}}, markets close early on 24 December.",
"email": true,
"subject": "Holiday trading hours",
"to_group": "real\\*"
}'json
{
"success": true,
"code": "ok",
"message": "done",
"data": {
"delivered": 1,
"emailed": 1,
"mail": {
"attachments": [
{}
],
"body": "string",
"created_at": "2026-09-25T10:15:00Z",
"draft": {
"email": "ann@example.com",
"reply_to": 1,
"to": 1,
"to_group": "string",
"to_logins": [
1
]
},
"folder": "string",
"from": 1,
"from_name": "string",
"id": 1,
"message_id": "3f2a9c1e-8b7d-4e6f-a5c4-1b2d3e4f5a6b",
"read": true,
"recipients": "string",
"subject": "string"
},
"skipped": 1
},
"request_id": "5b7f2c1e"
}ts
staff.mail.create(body: mail.StaffMessage, options?: RequestOptions): Promise<mail.Sent>drafts
POST
/admin/v1/mail/draftsSave a draft (staff)
Stores what the caller is writing in the draft folder and answers it as saved. id 0 (or left out) makes a new draft; an id of one of the caller's drafts rewrites it (same as PUT). Staff address a draft with to_logins (up to 1000) or to_group (a group mask, up to 128 characters) and may set email; a trader account uses to (a staff mailbox login from GET /trader/v1/mailboxes) and the staff fields are ignored. reply_to (a non-draft mail the caller holds) keeps the draft in that thread. Nothing is checked against scope or mailboxes here — sending does that — and the subject may still be empty; it must fit 256 characters on one line and the body 64 KiB. Upload files with POST …/mail/drafts/{id}/attachments. A trader whose group has mail switched off is refused.
More details
Rights: the staff member needs every one of: mailbox.
Request body
bodystringemailbooleanidintegerreply_tointegersubjectstringtointegerLogin is the numeric identity of every account, trader or staff.
to_groupstringto_loginsinteger[]Returns
Promise<Mail>The answer's
data.Errors
400
invalid_input: invalid input401
auth.session_expired: session expired, sign in again403
auth.csrf_invalid: request refused: reload the page and try again403
auth.invalid_terminal: this terminal cannot be used here403
auth.otp_enrol_required: your group requires one-time codes: set them up to continue403
auth.password_change_required: you must change your password first403
forbidden: you do not have access to this action404
not_found: not found500
internal: something went wrongts
import { staff } from "@/lib/noviqa"
const data = await staff.mail.drafts({
body: "Dear {{name}}, …",
email: true,
subject: "Margin requirements update",
to_logins: [
10001,
10002
]
})bash
curl -X POST "$NOVIQA_API_URL/admin/v1/mail/drafts" \
-H "Authorization: Bearer $NOVIQA_TOKEN" \
-H "Content-Type: application/json" \
--data-raw '{
"body": "Dear {{name}}, …",
"email": true,
"subject": "Margin requirements update",
"to_logins": [
10001,
10002
]
}'json
{
"success": true,
"code": "ok",
"message": "done",
"data": {
"attachments": [
{
"id": 1,
"name": "Ann Smith",
"size": 1
}
],
"body": "string",
"created_at": "2026-09-25T10:15:00Z",
"draft": {
"email": "ann@example.com",
"reply_to": 1,
"to": 1,
"to_group": "string",
"to_logins": [
1
]
},
"folder": "string",
"from": 1,
"from_name": "string",
"id": 1,
"message_id": "3f2a9c1e-8b7d-4e6f-a5c4-1b2d3e4f5a6b",
"read": true,
"recipients": "string",
"subject": "string"
},
"request_id": "5b7f2c1e"
}ts
staff.mail.drafts(body: mail.Draft, options?: RequestOptions): Promise<models.Mail>updateDrafts
PUT
/admin/v1/mail/drafts/{id}Rewrite a draft (staff)
Replaces the whole draft named in the URL (any id in the body is overridden by the URL). Same rules as POST …/mail/drafts; fields left out are cleared, not kept. Its uploaded files stay. Answers the draft as saved.
More details
Rights: the staff member needs every one of: mailbox.
Parameters
idinteger · pathrequiredNumeric id.
Request body
bodystringemailbooleanidintegerreply_tointegersubjectstringtointegerLogin is the numeric identity of every account, trader or staff.
to_groupstringto_loginsinteger[]Returns
Promise<Mail>The answer's
data.Errors
400
invalid_input: invalid input401
auth.session_expired: session expired, sign in again403
auth.csrf_invalid: request refused: reload the page and try again403
auth.invalid_terminal: this terminal cannot be used here403
auth.otp_enrol_required: your group requires one-time codes: set them up to continue403
auth.password_change_required: you must change your password first403
forbidden: you do not have access to this action404
not_found: not found500
internal: something went wrongts
import { staff } from "@/lib/noviqa"
const data = await staff.mail.updateDrafts(1, {
body: "Dear {{name}}, …",
subject: "Holiday trading hours",
to_group: "real\\*"
})bash
curl -X PUT "$NOVIQA_API_URL/admin/v1/mail/drafts/1" \
-H "Authorization: Bearer $NOVIQA_TOKEN" \
-H "Content-Type: application/json" \
--data-raw '{
"body": "Dear {{name}}, …",
"subject": "Holiday trading hours",
"to_group": "real\\*"
}'json
{
"success": true,
"code": "ok",
"message": "done",
"data": {
"attachments": [
{
"id": 1,
"name": "Ann Smith",
"size": 1
}
],
"body": "string",
"created_at": "2026-09-25T10:15:00Z",
"draft": {
"email": "ann@example.com",
"reply_to": 1,
"to": 1,
"to_group": "string",
"to_logins": [
1
]
},
"folder": "string",
"from": 1,
"from_name": "string",
"id": 1,
"message_id": "3f2a9c1e-8b7d-4e6f-a5c4-1b2d3e4f5a6b",
"read": true,
"recipients": "string",
"subject": "string"
},
"request_id": "5b7f2c1e"
}ts
staff.mail.updateDrafts(id: number, body: mail.Draft, options?: RequestOptions): Promise<models.Mail>draftsAttachments
POST
/admin/v1/mail/drafts/{id}/attachmentsUpload a file onto a draft (staff)
Uploads one file as multipart/form-data field file (the only field read; the part's file name becomes the attachment name, path stripped). The file must be 1 byte to 1 MB; with the files already on the draft at most 5 files and 2 MB together; the name must be a plain file name of up to 128 characters with an allowed extension (.png, .jpg, .jpeg, .bmp, .gif, .zip, .7z, .doc, .docx, .xls, .xlsx, .odt, .rtf, .csv, .txt, .log, .pdf). Sending the draft (with draft_id) sends its files. Answers the draft with its file list. A trader whose group has mail switched off is refused.
More details
Rights: the staff member needs every one of: mailbox.
Parameters
idinteger · pathrequiredNumeric id.
Returns
Promise<Mail>The answer's
data.Errors
400
invalid_input: invalid input401
auth.session_expired: session expired, sign in again403
auth.csrf_invalid: request refused: reload the page and try again403
auth.invalid_terminal: this terminal cannot be used here403
auth.otp_enrol_required: your group requires one-time codes: set them up to continue403
auth.password_change_required: you must change your password first403
forbidden: you do not have access to this action404
not_found: not found500
internal: something went wrongts
import { staff } from "@/lib/noviqa"
const data = await staff.mail.draftsAttachments(1)bash
curl -X POST "$NOVIQA_API_URL/admin/v1/mail/drafts/1/attachments" \
-H "Authorization: Bearer $NOVIQA_TOKEN"json
{
"success": true,
"code": "ok",
"message": "done",
"data": {
"attachments": [
{
"id": 1,
"name": "Ann Smith",
"size": 1
}
],
"body": "string",
"created_at": "2026-09-25T10:15:00Z",
"draft": {
"email": "ann@example.com",
"reply_to": 1,
"to": 1,
"to_group": "string",
"to_logins": [
1
]
},
"folder": "string",
"from": 1,
"from_name": "string",
"id": 1,
"message_id": "3f2a9c1e-8b7d-4e6f-a5c4-1b2d3e4f5a6b",
"read": true,
"recipients": "string",
"subject": "string"
},
"request_id": "5b7f2c1e"
}ts
staff.mail.draftsAttachments(id: number, options?: RequestOptions): Promise<models.Mail>deleteDraftsAttachments
DELETE
/admin/v1/mail/drafts/{id}/attachments/{file}Remove a file from a draft (staff)
Deletes one uploaded file from the caller's draft and answers the draft with its remaining files.
More details
Rights: the staff member needs every one of: mailbox.
Parameters
idinteger · pathrequiredNumeric id.
fileinteger · pathrequiredAttachment id, from the draft's
attachments.Returns
Promise<Mail>The answer's
data.Errors
400
invalid_input: invalid input401
auth.session_expired: session expired, sign in again403
auth.csrf_invalid: request refused: reload the page and try again403
auth.invalid_terminal: this terminal cannot be used here403
auth.otp_enrol_required: your group requires one-time codes: set them up to continue403
auth.password_change_required: you must change your password first403
forbidden: you do not have access to this action404
not_found: not found500
internal: something went wrongts
import { staff } from "@/lib/noviqa"
const data = await staff.mail.deleteDraftsAttachments(1, 1)bash
curl -X DELETE "$NOVIQA_API_URL/admin/v1/mail/drafts/1/attachments/1" \
-H "Authorization: Bearer $NOVIQA_TOKEN"json
{
"success": true,
"code": "ok",
"message": "done",
"data": {
"attachments": [
{
"id": 1,
"name": "Ann Smith",
"size": 1
}
],
"body": "string",
"created_at": "2026-09-25T10:15:00Z",
"draft": {
"email": "ann@example.com",
"reply_to": 1,
"to": 1,
"to_group": "string",
"to_logins": [
1
]
},
"folder": "string",
"from": 1,
"from_name": "string",
"id": 1,
"message_id": "3f2a9c1e-8b7d-4e6f-a5c4-1b2d3e4f5a6b",
"read": true,
"recipients": "string",
"subject": "string"
},
"request_id": "5b7f2c1e"
}ts
staff.mail.deleteDraftsAttachments(id: number, file: number, options?: RequestOptions): Promise<models.Mail>preview
POST
/admin/v1/mail/previewPreview a mail before sending
Takes the same body as POST /admin/v1/mail and runs the same checks and addressing in the caller's scope, without sending or storing anything: how many accounts it reaches, how many are skipped (mail switched off) and how many have an e-mail address, plus the subject and body as the first reached account reads them (placeholders filled). draft_id, reply_to and email are accepted but not used.
More details
Rights: the staff member needs every one of: mailbox.
Request body
attachmentsNewAttachment_input[]bodystringdraft_idintegerthe draft this is sent from; it is removed
emailbooleanalso send it by e-mail through the default mail server
reply_tointegera mail of the sender's this answers (0 = a new thread)
server_idintegerwhich mail server; 0 = the default one
subjectstringto_groupstringto_loginsinteger[]Returns
Promise<Preview>The answer's
data.Errors
400
invalid_input: invalid input401
auth.session_expired: session expired, sign in again403
auth.csrf_invalid: request refused: reload the page and try again403
auth.invalid_terminal: this terminal cannot be used here403
auth.otp_enrol_required: your group requires one-time codes: set them up to continue403
auth.password_change_required: you must change your password first403
forbidden: you do not have access to this action500
internal: something went wrongts
import { staff } from "@/lib/noviqa"
const data = await staff.mail.preview({
body: "Dear {{name}}, …",
subject: "Your account {{login}}",
to_logins: [
10001
]
})bash
curl -X POST "$NOVIQA_API_URL/admin/v1/mail/preview" \
-H "Authorization: Bearer $NOVIQA_TOKEN" \
-H "Content-Type: application/json" \
--data-raw '{
"body": "Dear {{name}}, …",
"subject": "Your account {{login}}",
"to_logins": [
10001
]
}'json
{
"success": true,
"code": "ok",
"message": "done",
"data": {
"body": "string",
"emails": 1,
"login": 1001,
"recipients": 1,
"skipped": 1,
"subject": "string"
},
"request_id": "5b7f2c1e"
}ts
staff.mail.preview(body: mail.StaffMessage, options?: RequestOptions): Promise<mail.Preview>retrieve
GET
/admin/v1/mail/{id}Read one mail (staff)
One message the caller holds (any folder), with its file list. Reading an unread inbox message marks it read. Download a file with GET …/mail/{id}/attachments/{file}.
More details
Rights: the staff member needs every one of: mailbox.
Parameters
idinteger · pathrequiredNumeric id.
Returns
Promise<Mail>The answer's
data.Errors
400
invalid_input: invalid input401
auth.session_expired: session expired, sign in again403
auth.invalid_terminal: this terminal cannot be used here403
auth.otp_enrol_required: your group requires one-time codes: set them up to continue403
auth.password_change_required: you must change your password first403
forbidden: you do not have access to this action404
not_found: not found500
internal: something went wrongts
import { staff } from "@/lib/noviqa"
const data = await staff.mail.retrieve(1)bash
curl -X GET "$NOVIQA_API_URL/admin/v1/mail/1" \
-H "Authorization: Bearer $NOVIQA_TOKEN"json
{
"success": true,
"code": "ok",
"message": "done",
"data": {
"attachments": [
{
"id": 1,
"name": "Ann Smith",
"size": 1
}
],
"body": "string",
"created_at": "2026-09-25T10:15:00Z",
"draft": {
"email": "ann@example.com",
"reply_to": 1,
"to": 1,
"to_group": "string",
"to_logins": [
1
]
},
"folder": "string",
"from": 1,
"from_name": "string",
"id": 1,
"message_id": "3f2a9c1e-8b7d-4e6f-a5c4-1b2d3e4f5a6b",
"read": true,
"recipients": "string",
"subject": "string"
},
"request_id": "5b7f2c1e"
}ts
staff.mail.retrieve(id: number, options?: RequestOptions): Promise<models.Mail>delete
DELETE
/admin/v1/mail/{id}Delete one mail (staff)
Removes the caller's own copy (inbox, sent or draft); other people's copies stay. The files go when the last copy of the message is gone.
More details
Rights: the staff member needs every one of: mailbox.
Parameters
idinteger · pathrequiredNumeric id.
Returns
Promise<null>The answer's
data.Errors
400
invalid_input: invalid input401
auth.session_expired: session expired, sign in again403
auth.csrf_invalid: request refused: reload the page and try again403
auth.invalid_terminal: this terminal cannot be used here403
auth.otp_enrol_required: your group requires one-time codes: set them up to continue403
auth.password_change_required: you must change your password first403
forbidden: you do not have access to this action404
not_found: not found500
internal: something went wrongts
import { staff } from "@/lib/noviqa"
const data = await staff.mail.delete(1)bash
curl -X DELETE "$NOVIQA_API_URL/admin/v1/mail/1" \
-H "Authorization: Bearer $NOVIQA_TOKEN"json
{
"success": true,
"code": "ok",
"message": "done",
"data": null,
"request_id": "5b7f2c1e"
}ts
staff.mail.delete(id: number, options?: RequestOptions): Promise<null>retrieveAttachments
GET
/admin/v1/mail/{id}/attachments/{file}Download a mail attachment (staff)
The bytes of one file of a message the caller holds a copy of, sent as application/octet-stream with Content-Disposition: attachment; filename=… and X-Content-Type-Options: nosniff: save it, never show it inline. The file ids are in the mail's attachments.
More details
Rights: the staff member needs every one of: mailbox.
Parameters
idinteger · pathrequiredNumeric id.
fileinteger · pathrequiredAttachment id, from the mail's
attachments.Returns
Promise<Response>The answer's
data.Errors
400
invalid_input: invalid input401
auth.session_expired: session expired, sign in again403
auth.invalid_terminal: this terminal cannot be used here403
auth.otp_enrol_required: your group requires one-time codes: set them up to continue403
auth.password_change_required: you must change your password first403
forbidden: you do not have access to this action404
not_found: not found500
internal: something went wrongts
import { staff } from "@/lib/noviqa"
const data = await staff.mail.retrieveAttachments(1, 1)bash
curl -X GET "$NOVIQA_API_URL/admin/v1/mail/1/attachments/1" \
-H "Authorization: Bearer $NOVIQA_TOKEN"ts
// application/octet-stream file: the SDK returns the raw Responsets
staff.mail.retrieveAttachments(id: number, file: number, options?: RequestOptions): Promise<Response>