Labels and system labels
Labels classify messages and threads, but they also expose service state and control visibility. Use caller-managed labels for your workflow, leave service-owned labels to AgentMail, and treat an absent restricted resource as a permissions question before treating it as missing data.
Use caller-managed labels for application classification
Caller-managed labels are lowercase string tags you apply to messages and threads, such as needs-review, customer-123, or processed. A resource can hold at most 256 labels. The message_update permission is required to change message labels and is also required to change thread labels.
| Label mechanism | Can the caller change this label? |
|---|---|
Caller-managed label, such as needs-review | Yes, by adding or removing it through a label-update request. |
Service-owned system label, such as received or delivered | No. AgentMail maintains it. |
Use add_labels and remove_labels to make a targeted change. This Python example adds a workflow label and removes an earlier workflow label from one message:
from agentmail import AgentMail
client = AgentMail()
updated = client.inboxes.messages.update(
inbox_id="support@example.com",
message_id="<message-id@example.com>",
add_labels=["needs-review"],
remove_labels=["triaged"],
)
print(updated.labels)The same change is available over PATCH /v0/inboxes/{inbox_id}/messages/{message_id} with add_labels and remove_labels in the JSON body. The response contains the message’s message_id and its resulting labels. Keep the labels in your own namespace so that they describe application state rather than transport or delivery state.
Recognize system labels as service-owned state
A caller cannot add or remove system labels through label creation or update input. Their presence is information to read, not a value to set.
A system label can coexist with caller-managed labels on the same resource. For example, a received message might carry both received and needs-review. Updating the workflow label must leave the system label unchanged.
Do not submit protected system labels
The following protected system labels are rejected when submitted in labels, add_labels, or remove_labels, with a 400 naming the label.
| Protected label | Why the request is rejected |
|---|---|
received | AgentMail assigns inbound receipt state. |
sent | AgentMail assigns outbound send state. |
bounced | AgentMail maintains this delivery-state label. |
complained | AgentMail maintains this delivery-state label. |
delayed | AgentMail maintains this delivery-state label. |
delivered | AgentMail maintains this delivery-state label. |
rejected | AgentMail maintains this delivery-state label. |
opened | AgentMail records the first open on a tracked message. |
scheduled | AgentMail mirrors a draft’s scheduled send state (send_at). |
This page does not cover the deliverability conditions behind labels such as bounced or complained. Use the deliverability concepts documentation for those conditions.
Request permission before reading restricted labels
The restricted labels spam, blocked, unauthenticated, and trash are visibility controls. The matching permission for each is a boolean API-key field, set when the key is created or updated.
| Restricted label | Required read permission |
|---|---|
spam | label_spam_read |
blocked | label_blocked_read |
unauthenticated | label_unauthenticated_read |
trash | label_trash_read |
List requests exclude these labels by default. To include one, pass its matching query option, such as include_spam=true, and use a credential with the matching read permission. Both conditions are required.
A direct lookup without the required label-read permission also reports the resource as not found. That response intentionally does not distinguish a hidden resource from an incorrect identifier. Check the credential’s scope, the relevant label permission, and the list request’s include option before assuming data was deleted.
Use inbox events to audit label changes
List inbox events with GET /v0/inboxes/{inbox_id}/events; results are reverse chronological by default. Each event identifies the event_type (label.added or label.removed), message_id, changed label, event_at, and event_id.
Use this trail to establish which message changed, whether a label was added or removed, and when the change occurred. Paginate through the event list when reconstructing a longer history.