Repository navigation
Releases: mailtea-app/mailtea-python
Release list
v0.23.0
- Changed (API):
emails.inbound.replypassestoandheadersthrough as it always did, but the API now acts on them instead of dropping them: atonaming anyone the original did not carry raises on a 422reply_target_not_allowed, and any header other thanAuto-SubmittedandX-Auto-Response-Suppressraises on a 400. The docstring documents both and the plan-limitcodevalues. - Added:
api_keys.current()describes the calling key. - Changed (docs):
emails.inbound.replydocumentsattachments;emails.getdocumentsmessage_id;emails.inbound.getdocuments the arrival checks. The fields already passed through.
Released from mailtea-app/mailtea@615cef4.
v0.22.0
- Changed (API):
posts.send, andposts.createwithsend=True, send to
the whole audience. They used to stop at 10,000 recipients without saying
so. One send can reach at most 25,000; a larger audience raises on a 422
audience_too_largethat carriesaudience_countandmax_recipients,
and nothing is sent or scheduled. Onposts.createthe error body carries
the draft'sid. A send also raises on a 409post_changedwhen the post
was scheduled or edited elsewhere while the request was preparing it. No
package change: this is the API's behaviour once it is deployed.
Released from mailtea-app/mailtea@1d368b5.
v0.21.0
- Changed (API):
posts.send, andposts.createwithsend=True, now raise
on a 403system_domain_recipient_restricted(withrestriction) when the
team has no verified sending domain and the audience includes anyone outside
the team, or the post sends from the built-in*.mailtea.emailaddress.
Nothing is sent or scheduled and the post stays a draft; onposts.create
the error body carries the draft'sid. The send used to be accepted and
fail a moment later. A broadcast (kind="broadcast") is never published to
the website, however it is sent. No package change: this is the API's
behaviour once it is deployed.
Released from mailtea-app/mailtea@68b43c2.
v0.20.0
- Changed (API):
contacts.list(search=...)with several whole addresses
separated by commas or spaces now returns exactly those contacts (up to
200), the same as Mailtea Studio and MCP. A list with an entry that is not
a whole address is refused with a 400 naming it. Part of an address still
matches as before. The docstring says so. - Added:
purposeondomains.claims.create("email","site"or"both",
default"email") and on every claim reply. Asiteclaim gets no sending
identity. The package passes it through as it is; this release documents it.
Needs the API deployed with this change. - Added:
segment_idonposts.createandposts.update(Noneclears it
on update) and on every post reply, to send a post to one segment of its
publication instead of all active contacts.inactive_dayson
segments.createandsegments.update(Noneclears it) and on every
segment reply: contacts with no open or click in the last N days (1 to
3650), counting contacts who never engaged, which is the silent cohort and
not engaged readers. The package passes both through as it is; this release
documents them. They need the API deployed with this change. - Changed:
segments.deletedocuments that a segment a draft, scheduled or
sending post targets cannot be deleted: it raisesMailteaErrorwith
status 409 andcodesegment_in_use. Deleting one used to send those
posts to everyone.
Released from mailtea-app/mailtea@1ea0e1b.
v0.19.0
- Changed: template history records the sender. Each entry from
templates.versions()carriesfrom(read it asentry["from"]),
reply_toandsender_recorded, an update that changes only the From or
Reply-To records a version (or folds into the open one, like any edit), and
templates.restore_version()brings the version's From and Reply-To back
with the design. A version withsender_recordedFalsewas recorded
before this change and leaves the current From and Reply-To alone when
restored. The behaviour comes from the API and reaches every package version
on deploy; this release updates the docstrings.
Released from mailtea-app/mailtea@ff8e7ef.
v0.18.0
-
Added: optimistic-concurrency tokens for editing over an API that may also
be edited in Mailtea Studio or by another agent.templates.updateand
templates.publishacceptbase_revision(read from a template's
revision, now on every template reply); a stale value answers with
MailteaError(codestale_write, the response body also carrying
current_revision) and nothing is saved.automations.updateaccepts
base_versionthe same way for graph writes (steps), answeringcode
stale_version.posts.updateacceptsbase_updated_at(read from a
post'supdated_at, now also returned byposts.updateitself), answering
codestale_writewithcurrent_updated_at. All three are optional;
omit them for today's unconditional write. -
Changed:
posts.create/posts.updatekeep thefrom(keywordfrom_=)
andreply_toyou pass, andposts.getreturns them (Nonewhen the named
sender or the publication default decides).nameis only the post's
internal name and no longer overwrites the subject.posts.updatechanges
only the fields you pass;""clearsname,fromorreply_to. -
Note: a
fromthat is not on one of the publication's verified sending
domains is refused with a 422, andreply_tomust be a valid address. -
Docs:
automations.activatelists the two cloud-onlyno_verified_sender
reasons,CUSTOM_DOMAIN_REQUIREDand the newBUILT_IN_SENDER(a step sends
from the built-in{slug}.mailtea.emailaddress). No runtime change. -
Breaking:
posts.create()withtemplate_idnow HTML-escapes the
variablesyou pass, the same as every other send. HTML passed in a
{{key}}value now arrives as visible text, and a value you escaped
yourself arrives double-escaped. Put{{{key}}}in the template where a
value is meant to be raw HTML. Variables are now filled in both the
{{key}}and Visual Email Designer{key}forms. A declared variable you
do not pass stays in the post with itsfallback_value, so the broadcast
gives each recipient their own value or that fallback, and undeclared tokens
like{{contact.first_name}}are left for the broadcast too. The post keeps
the template's published page style, is wrapped in that page, and has its
show-if blocks decided per recipient when it is sent. Before, only the
variables you passed were replaced, raw, and only in{{key}}form. It uses
the template's published version; Mailtea Studio's "Use template" starts
from the latest saved design instead. -
Changed:
templates.update()andtemplates.restore_version()no longer
move a published template back to draft. The template keeps its published
status, and automations and the API keep sending its published version until
templates.publish()is called again. The template'sfromandreply_to
are part of the published version too, so a new sender or reply-to address
reaches sends only after the next publish.templates.unpublish()is now
the only way to stop a published template sending, short of deleting it, and
it drops the stored published version so the next publish starts from the
current content. -
Added:
has_unpublished_versionson every returned template. True only when
the template is published and its saved content (From, Reply-To and the
style profile included) differs from the published version. -
Added:
is_publishedon each template version entry: true for the one entry
automations and the API are sending now.is_currentis now described as
what it is: the entry that matches the working copy (the saved design being
edited), not necessarily what is sending.is_publishedis false on every
entry of a draft, and on a template published before the field existed until
it is published again. -
Changed: the
unpublishedfield on the update and restore replies is kept
for compatibility and is now alwaysFalse. Check
has_unpublished_versions(or the reply'smessage) instead. -
Changed (API behavior): a template variable's
fallback_valuecan no longer
contain{or}. Creating a template with one, or changing a fallback
to one on update, is a 400 ("Fallbacks can't contain { or }."). A value
the template already stores is accepted unchanged, so a template saved
before the rule keeps saving. Inline chip fallbacks such as
{first_name|Mom & Pop}now render as written instead of double-escaped. -
Changed (API behavior): saving an active automation is refused only when the
edit adds an error the live version does not already have. The 422
active_graph_invalidreply'sissueslists just those new problems.
Before, any error refused the save, even one the live version already had.
Starting refuses every error as before, except anunknown_step_refat a
config.*path or a triggermissing_branchthat the version the
automation last ran on already had, so pausing and starting an unchanged
automation keeps working. Issues the last live version already had come back
withpre_existing: true. -
Added (API behavior): issue objects carry
field, what a rule reads (the
rule'sfield, or the path in a{"var": ...}value, e.g.
steps.welcome.opened) when the issue is about one. -
Changed (API behavior): two issues are the same problem when their code and
step match, and theirfieldor, when there is none, theirpath. Moving
a rule, by removing a rule beside it or putting it in a group, no longer
makes a problem the live version already had look new. An error is
pre_existingonly if the live version had an error there, not a warning. -
Changed (API behavior):
validate_onlyon an active automation answers the
way the save would. A trigger change is a 422trigger_locked_while_active,
a change that adds a problem is a 422active_graph_invalidlisting only
the new problems, and otherwise issues come back withpre_existingmarked
against the version live now. Before, it returned every issue unmarked. -
Changed (API behavior): changing the trigger (its type or key) of an active
automation is now refused with 422trigger_locked_while_active. Pause it
first; draft and paused automations can still change their trigger. Before,
the change was accepted. -
Added (API behavior): new validation rules. A trigger with nothing after it
is amissing_brancherror atbranches.next. A rule or{"var": ...}
value that readssteps.<key>.*for a step that isn't in the automation is
anunknown_step_referror at thatconfig.*path, or a warning when the
{"var": ...}has adefault. A rule or value that reads
event.properties.*when the automation does not start from an app event is
the new warningevent_field_without_event_trigger. -
Changed (API, no client change needed):
emails.send(template=...)may
leave outsubject,from_andsender_id. The template's published
subject is used, and its sender is the publication's default sender, then
the template's own From. Thesenddocstring now says so. -
Behaviour (API): the template's variables fill the subject with the same
values and fallbacks as the body.
Released from mailtea-app/mailtea@acea165.
v0.12.0
- Added: test mode.
api_keys.create(name=..., mode="test")mints a test key
(prefixedmt_test_) whose sends are validated, recorded and webhook-emitting
but never delivered, so CI can run against production Mailtea with your real
code and your real webhook handler. A test key is not a data sandbox — it
reads and writes your real contacts, templates, senders and webhooks. Only
delivery is simulated. - Added:
emails.list(mode="test")reads test-mode mail, and every email
carriesmode. There is no mixed view: a test key reads only test emails and
a live key only live ones. - Reserved recipients on
test.mailtea.emailforce an outcome:delivered@,
bounced@,complained@,delayed@,failed@. The firsttorecipient
decides; anything else is delivered. - Note:
modeis never accepted on a send. The key decides.
Released from mailtea-app/mailtea@8c362fb.
v0.11.0
- Changed:
automations.activate()documents theno_verified_senderrefusal.
A 422 with that code means asend_emailstep has no sender it can send
from;reason(NO_SENDER,DOMAIN_NOT_VERIFIED,WRONG_PURPOSE,
DKIM_NOT_VERIFIEDorINVALID_FROM) says which, andsteps[]names every
blocking step. Previously onlyautomation_invalidwas documented. - Added:
domains.update(id, tracking_subdomain=None)removes a tracking
subdomain. The domain's links go back to being served from the Mailtea host.
Links in mail you have already sent point at the old hostname and stop
resolving — there is no way to reinstate them. TheNonereaches the wire as
an explicitnull, so omitting the key and passingNoneare different
requests. An empty string is neither: it is refused with
tracking_subdomain_invalid. - Changed: the
MXrow inrecordsnow reports what the last verify found,
instead of readingpendingon every request but the verify itself. A domain
nobody has verified readsnot_started.
Released from mailtea-app/mailtea@fed0718.
v0.10.0
-
Added:
domains.claims—create(),get(),verify()andcancel(). When
adding a domain is refused with codedomain_held_elsewhere, another
publication holds the host; publish one TXT record to prove you control its
DNS and the domain moves to you. -
Documented:
domains.create()takesregion(fixed at creation),tlsand
tracking_subdomain, anddomains.list()filters onregionandstatus.
The resource forwards whatever you pass, so these worked already — this
release is where they are stated and covered by tests. -
Added:
contacts.set_property_values()andcontacts.list_property_values().
These write and read the per-contact values behind{{contact.<key>}}merge
tags — previously only possible from the dashboard, so a script could define a
property and never fill it in. Identify each value bykey(the name in your
template) orproperty_id, not both; an emptyvalueclears it and restores
the property'sfallback_value. -
Documented:
assets.uploadaccepts SVG (image/svg+xml); the docstring said it
was refused. The API has accepted it, served under a sandboxing CSP, since the
asset library shipped. Docs-only, no behaviour change.
Released from mailtea-app/mailtea@1dff0ba.
v0.9.1
- Documented: the API now enforces your plan's analytics retention window on
from_date. It is clamped to 30 days on most plans and 90 on Scale and
Enterprise; a value reaching further back returns data from the start of that
window rather than an error, and omitting it returns the window rather than
all time. No code change is required — this release only makes the behaviour
visible where you read it. - Changed: the list and analytics responses now report the window actually used
infrom_date, so a clamped request is visible rather than silently short. - Changed:
emails.list()andemails.analytics()docstrings carry it.
Released from mailtea-app/mailtea@c311c79.