Skip to main content

Addresses

The identifier as a first-class resource — created before any message arrives, and the one place a customer's own admission decision lives.

Overview

An address is what an identifier names: a place a conversation can happen — sometimes a person's DM, sometimes a room a Discord server shares. It is project-scoped, keyed by the prefixed, self-describing identifier a channel adapter produces (whatsapp:dm:<phone>, discord:dm:<user_id>, discord:thread:<guild_id>:<channel_id>).

PUT /v1/projects/{project_id}/addresses/{identifier} is the write a customer's own application makes when it learns something about an identifier — a payment cleared, a plan lapsed, an abuse report. It is a one-call upsert keyed by the identifier: no read first, and no 404 for one that has never written, because at the moment a payment clears the customer's backend knows the identifier and does not know whether naturali has ever seen it.

See the OpenAPI spec for the full endpoint and schema reference, or browse it rendered under API Reference → Addresses.

Data Model

Address

FieldTypeDescription
idstringPublic address ID.
project_idstringThe owning project.
identifierstringThe prefixed, self-describing channel identifier.
display_namestring, nullableA human-friendly name, as the channel reported it.
actor_idstring, nullableThe runtime actor this address speaks as; null until it has needed one.
actionstring, nullableThis address's own decision — agent, message, silence, or null. Beats every route.
agent_idstring, nullableThe agent that answers, when action is agent.
textstring, nullableThe fixed text delivered, when action is message.
repeatstring or object, nullableevery (default), once, or { after_seconds }.
languagestring, nullablePreferred reply language.
configobject, nullableConversation config.
created_atstring (date-time)
updated_atstring (date-time)

Key Concepts

The address's action beats everything

Resolution checks the address first: if it has an action, that wins outright, ahead of every route and the channel default. action: silence is how you block someone — abuse, an erasure in flight. action: null forgets the exception, so the route table decides again.

repeat, for a message action

Left alone, ten inbound messages produce ten identical refusals. every (the default) delivers on every inbound and writes no state; once delivers once then stays silent; { "after_seconds": N } delivers at most once per window. Editing the text, or resolving to a different action, resets the clock immediately.

Erasure is narrower than "erase this human"

DELETE /v1/projects/{project_id}/addresses/{identifier} removes the address, its conversations, and its runtime actor and sessions. Without a merge graph, naturali does not know that two identifiers are the same person, so this is a promise about this address, not about a human across every channel they have ever used.

Examples

Admit an identifier the moment a payment clears — one call, no prior read:

naturali set-address-action \
--project-id proj_V1StGXR8Z5jdHi6B \
--identifier instagram:dm:17841400000000000 \
--action agent \
--agent-id agent_V1StGXR8Z5jdHi6B

Hold a non-subscriber with a paywall message instead of an agent, once:

naturali set-address-action \
--project-id proj_V1StGXR8Z5jdHi6B \
--identifier instagram:dm:17841400000000000 \
--action message \
--text 'Subscribe at https://example.com/pricing' \
--repeat once

Erase an address — its conversations, actor and sessions with it:

naturali delete-address \
--project-id proj_V1StGXR8Z5jdHi6B \
--identifier instagram:dm:17841400000000000