API for your own code.

Read the mail in a workspace, add internal notes, assign and close conversations. One key, one header, JSON in and out. Version v1; a change that would break your code arrives as a new version, not as a surprise.

Keys

A workspace owner makes keys under Settings, API keys. A key has a name, an expiry date and one of two levels: read only, or read and change. You can have several, so a script that only reports never needs the right to close anything.

The key is shown once, when it is created. We keep only a hash of it, so a copy of our database is not a set of working keys. Lost one? Revoke it and make another.

Send it on every request:

curl https://coffer.email/api/v1/me \
  -H "Authorization: Bearer cof_your_key_here"

Base address: https://coffer.email/api/v1

Endpoints

  • GET/api/v1/meRead only

    Check the key and see which workspace it opens

    The first call to make. Confirms the key works, names the workspace and says what the key may do. Also tells you whether the workspace is read only because it is scheduled for deletion.

    Returns workspace (id, name, readOnly) and token (name, scope).

  • GET/api/v1/mailboxesRead only

    Mailboxes connected to the workspace

    Every mailbox with the id you need for the mailbox filter on conversations.

    Returns data: id, address, name, receiveMode, sendMode, status, color.

  • GET/api/v1/tagsRead only

    Tags used in the workspace

    The slug is what the tag filter on conversations expects.

    Returns data: id, name, slug, color.

  • GET/api/v1/membersRead only

    People in the workspace

    Ids from here are what assign and the author of a note expect. Names repeat and change; ids do not.

    Returns data: id, name, email, role.

  • GET/api/v1/contactsRead only

    Customers who have written in

    Contacts hidden in the panel are left out, exactly as they are in the customer list.

    qquery
    string Part of an address, a name or a company.
    limitquery
    integer 1 to 200, 50 by default.

    Returns data: id, email, name, company, phone, note, conversationCount, createdAt.

  • GET/api/v1/conversationsRead only

    List conversations, newest message first

    Paging uses a cursor, not page numbers: mail keeps arriving, so page two would show you the same rows again. Take nextBefore from the response and send it back as before. When nextBefore is null, you have everything.

    statusquery
    open | closed Only open or only closed conversations.
    mailboxquery
    string Mailbox id from /mailboxes.
    tagquery
    string Tag slug from /tags.
    assigneequery
    string Member id from /members.
    qquery
    string Text in the subject, the preview or the contact.
    spamquery
    boolean true returns spam instead of the normal list.
    limitquery
    integer 1 to 100, 25 by default.
    beforequery
    ISO 8601 nextBefore from the previous page.

    Returns data: conversations with mailbox, contact, assignee and tags. nextBefore: cursor or null.

  • GET/api/v1/conversations/{id}Read only

    One conversation with its whole thread

    Messages in the order they arrived, plus internal notes with their replies. A message with draft: true was written in the panel and never sent. An id from another workspace answers 404, not 403.

    idpath, required
    string Conversation id.

    Returns The conversation, plus messages and notes.

  • POST/api/v1/conversations/{id}/notesRead and change

    Add an internal note

    Notes are what the team says to each other beside the thread; the customer never sees them. authorId is required, because a note is a sentence from a person and a key is not a person.

    idpath, required
    string Conversation id.
    bodybody, required
    string The note, up to 5000 characters.
    authorIdbody, required
    string Member id from /members.
    mentionIdsbody
    string[] Member ids to point at. They see the conversation under Mentioning me.

    Returns The created note: id, body, createdAt, mentionIds. Status 201.

  • POST/api/v1/conversations/{id}/assignRead and change

    Give a conversation to someone, or take it off them

    Send memberId: null to unassign. The history line says a program did it, not a person - we do not invent a click that never happened.

    idpath, required
    string Conversation id.
    memberIdbody, required
    string | null Member id from /members, or null to unassign.

    Returns id and the new assigneeId.

  • POST/api/v1/conversations/{id}/statusRead and change

    Close a conversation or open it again

    Closing always says how it ended - a closed conversation without an outcome is a statistic nobody can use.

    idpath, required
    string Conversation id.
    statusbody, required
    open | closed What it becomes.
    outcomebody
    resolved | not_resolved Required when closing, ignored when opening.

    Returns id, status and outcome.

Errors

Every failure has the same shape, so you write the handling once. code is for your program and never changes; message is for whoever reads the log and may be reworded.

{ "error": { "code": "unauthorized", "message": "Missing or invalid API token…" } }
  • 401unauthorizedNo key, a key that never existed, one that expired or was revoked.
  • 403forbiddenA read-only key tried to change something.
  • 403read_onlyThe workspace is scheduled for deletion, so nothing can change.
  • 404not_foundNo such conversation in this workspace.
  • 400invalid_requestA parameter is missing or has a shape we cannot use.

Something missing? Write to [email protected] and say what you are building - an endpoint we have not written yet is easier to add before someone works around its absence.