openemail.automations
Jede Methode in diesem Namespace: ihre Signatur, ihre Parameter, was sie zurückgibt und ein Beispiel.
Methoden
Emails and other steps that run by themselves for each contact, such as a welcome series or a birthday note: build a draft from a starter or your own trigger and steps, publish, pause, resume and archive it, send a test, read its versions and statistics, and enroll contacts or take them out.
automations.list()automations.list_all()automations.iterate()automations.create()automations.list_starters()automations.get()automations.update()automations.delete()automations.publish()automations.pause()automations.resume()automations.archive()automations.duplicate()automations.send_test()automations.list_versions()automations.restore_version()automations.stats()automations.list_enrollments()automations.list_all_enrollments()automations.iterate_enrollments()automations.get_enrollment()automations.enroll()automations.exit_enrollment()
automations.list()
List one page of the automations in the workspace
def list( *, limit: int | None = None, cursor: str | None = None, status: AutomationStatus | None = None, api_key: str | None = None, timeout: float | None = None,) -> Page[AutomationResource]Returns one page of the automations the caller can reach, the most recently saved first, without their definitions or settings. Paging is keyset: limit= takes 1 to 100 and defaults to 50, and nextCursor goes back as cursor= while hasMore is True. list_all and iterate do that walk for you.
Each automation carries its status, triggerKind, how many steps and emails the draft holds, hasUnpublishedChanges, publishedVersion and counts: the contacts in it now, and the ones that completed or left in all its time, counted at the moment of the read. A paused automation says why in pausedReason.
Read one with get for the draft definition, the published one, the settings and the problems that would stop a publish.
Parameter
limitintRows per page, a whole number from 1 to 100. The server defaults to 50.
cursorstrThe
nextCursorfrom the previous page, passed back exactly as it came. One that names no automation is a 400invalid_cursor.statusAutomationStatusOnly automations in this state:
draft,live,pausedorarchived.AUTOMATION_STATUSESnames them.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
Page[AutomationResource], a dict with items, hasMore and nextCursor. Each item has id, name, description, status, triggerKind, stepCount, emailCount, hasUnpublishedChanges, publishedVersion, pausedReason, lastError, counts, createdBy, publishedAt, pausedAt, archivedAt, createdAt and updatedAt.
Beispiel
from openemail import openemail page = openemail.automations.list(status='live') for automation in page['items']: print(automation['name'], automation['triggerKind'], automation['counts']['active']) if page['hasMore']: print('next page starts after', page['nextCursor'])Hinweise
An app a member connected lists only the automations that member made. An API key and an app the owner connected list every automation in the workspace.
Read only, so the SDK retries it after a network failure like any other read.
Auch verfügbar über
automations.list_all()
Collect every automation you can reach into one list
def list_all( *, limit: int | None = None, cursor: str | None = None, status: AutomationStatus | None = None, api_key: str | None = None, timeout: float | None = None,) -> builtins.list[AutomationResource]Follows nextCursor from page to page and returns every automation the caller can reach as one list, the most recently saved first, in the shape list returns. limit= sets the page size of each request, not the total.
Parameter
limitintPage size per request, from 1 to 100, defaulting to 50 on the server.
cursorstrA
nextCursorfrom an earlier page to start after.statusAutomationStatusOnly automations in this state:
draft,live,pausedorarchived.api_keystrOverrides the client's API key for every page of this walk.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
list[AutomationResource] holding every automation across all pages.
Beispiel
from openemail import openemail automations = openemail.automations.list_all() paused = [automation for automation in automations if automation['status'] == 'paused'] for automation in paused: print(automation['name'], automation['pausedReason'])Hinweise
A failure on any page raises, and the automations already fetched are discarded.
Auch verfügbar über
automations.iterate()
Stream the automations you can reach one at a time
def iterate( *, limit: int | None = None, cursor: str | None = None, status: AutomationStatus | None = None, api_key: str | None = None, timeout: float | None = None,) -> Iterator[AutomationResource]Returns a generator that yields automations one by one, the most recently saved first, and requests the next page only once the current one is used up. Breaking out of the loop stops the requests.
Parameter
limitintPage size per request, from 1 to 100, defaulting to 50 on the server.
cursorstrA
nextCursorfrom an earlier page to start after.statusAutomationStatusOnly automations in this state:
draft,live,pausedorarchived.api_keystrOverrides the client's API key for every page of this walk.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
Iterator[AutomationResource], a generator yielding one automation per step.
Beispiel
from openemail import openemail for automation in openemail.automations.iterate(): if automation['hasUnpublishedChanges']: print('Unpublished edits:', automation['name'])Hinweise
The generator is lazy, so an abandoned loop costs only the pages it read.
Auch verfügbar über
automations.create()
Create an automation as a draft
def create( body: AutomationCreate, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationDetailResourceMakes a draft automation and returns it whole, in the shape get returns. Its trigger and steps come from definition when you send one, from the starter named in starter when you do not, and otherwise the draft starts empty. settings changes any of the defaults.
A draft may be incomplete: problems in the answer lists what is still missing, such as a template or a from address a starter leaves for you. Nothing runs until publish, which needs a complete draft and emails:send.
Parameter
body['name']strErforderlichWhat the workspace calls the automation, trimmed, 1 to 120 characters. Contacts never see it, and it need not be unique.
body['description']str | NoneA note for the workspace, at most 500 characters. Contacts never see it.
body['starter']strBegin from a starter, by the
sluglist_startersreturns:welcome-series,trial-follow-up,birthdayorwin-back. Ignored whendefinitionis sent. An unknown slug is 422invalid_automation.body['definition']AutomationDefinitionThe trigger and the steps, in the shape
getreturns asdefinition.triggersays what starts the automation:audience_joinedwithaudienceIdandincludeImported,form_submittedwithformId,eventwitheventNameandfilters,datewithfield,audienceIdandoffsetDays, ormanual.entryis the key of the first step, andstepsholds every step:send_email,wait,branch,add_to_audience,remove_from_audience,update_field,webhookorexit. Each step has a uniquekeyof 3 to 24 lowercase letters and digits starting with a letter, and names the next step by key innext, or inyesandnofor a branch.Noneends the path. Paths never join or loop, and a definition holds at most 50 steps. Leave it out to usestarteror an empty draft.body['settings']['timezone']strThe IANA time zone the sending window and waits until a day and time are read in, such as
Europe/London. Defaults toUTC.body['settings']['sendWindow']AutomationSendWindow | NoneWhen emails may go out:
daysof the week, where 0 is Sunday, andstartMinutetoendMinuteof the day, counted from midnight. An email due outside it waits for the window to open.None, the default, sends at any time.body['settings']['reentryDays']int | NoneHow many days after a contact finishes before they may enter again, from 1 to 3650.
None, the default, lets each contact through once only.body['settings']['exitOnLeave']boolTake a contact out when they leave the audience that started the automation. Defaults to
True.body['settings']['listAudienceId']str | NoneThe audience an unsubscribe from one of these emails is recorded in.
None, the default, uses the audience that starts the automation, or the built-in audience of every contact when no audience starts it. An id that is not an audience of the workspace is 422invalid_automation.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationDetailResource: the AutomationResource fields with status draft, plus definition, published, which is None, settings and problems.
Beispiel
from openemail import openemail automation = openemail.automations.create( { 'name': 'Welcome series', 'starter': 'welcome-series', 'settings': {'timezone': 'Europe/London'}, }) for problem in automation['problems']: print(problem['path'], problem['message'])Hinweise
A value of the wrong type or length is 422
invalid_parameterwithparamnaming it, and a key the body does not take is 422unknown_parameter. A definition that breaks a rule of its structure, such as two steps with one key or two paths into one step, is 422invalid_automation, andproblemson the error lists each one.A workspace holds a limited number of automations, drafts included, and support can raise that for a workspace that needs more. The next one past the limit is 422
workspace_limit_reached.Not retried automatically, and nothing deduplicates by name, so a retry after a lost response can leave two drafts. List them and delete the spare.
Auch verfügbar über
automations.list_starters()
List the ready-made automations to start from
def list_starters( *, api_key: str | None = None, timeout: float | None = None,) -> builtins.list[AutomationStarterResource]Returns the starting points the app offers when somebody makes an automation: a welcome series, a trial follow-up, a birthday note and a win-back. Each comes with its whole definition, so you can read it, change it and send it to create, or pass its slug to create as starter.
A starter leaves the audience, the templates and the from address as empty strings for you to fill in. The list is the same for every workspace and is not paginated.
Parameter
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
list[AutomationStarterResource], each with slug, name, description and definition.
Beispiel
from openemail import openemail starters = openemail.automations.list_starters() for starter in starters: print(starter['slug'], starter['description'])Hinweise
Read only, so the SDK retries it after a network failure like any other read.
Auch verfügbar über
automations.get()
Retrieve an automation with its definition and settings
def get( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationDetailResourceReturns the whole automation: the draft definition you edit, published, the definition of the version that is running, settings, fresh counts and problems.
problems is what is wrong with the draft at the moment of the read, each with a code, the path of the field, the stepKey, a message and blocking, which is False for a warning. An empty list means the draft is ready, though publish checks more: that every template can be sent and that the caller may send from every from address.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationDetailResource: id, name, description, status, triggerKind, stepCount, emailCount, hasUnpublishedChanges, publishedVersion, pausedReason, lastError, counts, createdBy, publishedAt, pausedAt, archivedAt, createdAt and updatedAt, plus definition, published, settings and problems.
Beispiel
from openemail import openemail automation = openemail.automations.get('aut_5c1e9a7b3d2f48e6a0b4c7d1')trigger = automation['definition']['trigger'] print(automation['status'], len(automation['definition']['steps'])) if trigger is not None: print('starts on', trigger['kind']) for problem in automation['problems']: if problem['blocking']: print(problem['message'])Hinweise
An id that names no automation the caller can reach is 404
automation_not_found, whether it does not exist or belongs to another workspace.Read only, so the SDK retries it after a network failure like any other read.
Auch verfügbar über
automations.update()
Change the draft, the settings or the name of an automation
def update( id: str, patch: AutomationPatch, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationDetailResourceA partial update that returns the automation whole. definition replaces the draft, and a live automation keeps running its published version until you publish again, so an edit never changes the path of a contact who is halfway through. settings is merged field by field and takes effect at once.
Read the automation, change it and send expectedUpdatedAt with the updatedAt you read. If someone saved it in between, the call is refused with 409 version_conflict instead of writing over their change.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.patch['name']strNew name, trimmed, 1 to 120 characters.
patch['description']str | NoneNew note, at most 500 characters.
Noneclears it.patch['definition']AutomationDefinitionThe new draft, whole. The trigger and the steps, in the shape
getreturns asdefinition.triggersays what starts the automation:audience_joinedwithaudienceIdandincludeImported,form_submittedwithformId,eventwitheventNameandfilters,datewithfield,audienceIdandoffsetDays, ormanual.entryis the key of the first step, andstepsholds every step:send_email,wait,branch,add_to_audience,remove_from_audience,update_field,webhookorexit. Each step has a uniquekeyof 3 to 24 lowercase letters and digits starting with a letter, and names the next step by key innext, or inyesandnofor a branch.Noneends the path. Paths never join or loop, and a definition holds at most 50 steps.patch['settings']['timezone']strThe IANA time zone the sending window and waits until a day and time are read in, such as
Europe/London. Defaults toUTC.patch['settings']['sendWindow']AutomationSendWindow | NoneWhen emails may go out:
daysof the week, where 0 is Sunday, andstartMinutetoendMinuteof the day, counted from midnight. An email due outside it waits for the window to open.None, the default, sends at any time.patch['settings']['reentryDays']int | NoneHow many days after a contact finishes before they may enter again, from 1 to 3650.
None, the default, lets each contact through once only.patch['settings']['exitOnLeave']boolTake a contact out when they leave the audience that started the automation. Defaults to
True.patch['settings']['listAudienceId']str | NoneThe audience an unsubscribe from one of these emails is recorded in.
None, the default, uses the audience that starts the automation, or the built-in audience of every contact when no audience starts it. An id that is not an audience of the workspace is 422invalid_automation.patch['expectedUpdatedAt']strThe
updatedAtyou read, as an ISO 8601 instant. When the automation was saved since, the call is refused with 409version_conflictand nothing is written.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationDetailResource as it is after the save, with a new updatedAt and, when the draft changed, hasUnpublishedChanges set to True on a published automation.
Beispiel
from openemail import openemail current = openemail.automations.get('aut_5c1e9a7b3d2f48e6a0b4c7d1') saved = openemail.automations.update( current['id'], { 'settings': { 'sendWindow': {'days': [1, 2, 3, 4, 5], 'startMinute': 540, 'endMinute': 1020} }, 'expectedUpdatedAt': current['updatedAt'], },) print(saved['settings']['sendWindow'], saved['updatedAt'])Hinweise
An archived automation cannot be changed and answers 409
automation_archived.A value of the wrong type or length is 422
invalid_parameter, a key the patch does not take is 422unknown_parameter, and a definition that breaks a rule of its structure is 422invalid_automationwithproblemson the error.Retried automatically on network failure and retryable statuses, since saving the same patch twice leaves the same automation. With
expectedUpdatedAt, a retry after a lost response answers 409version_conflict: read the automation to see that your change is there.
Auch verfügbar über
automations.delete()
Delete an automation with its versions and history
def delete( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> DeletedAutomationResourceDeletes the automation with its versions, its enrollments and its statistics. Contacts in it stop at once and get nothing more. The emails it already sent stay in emails.list.
There is no undo. To retire an automation and keep its history, use archive.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
DeletedAutomationResource, a dict with object set to automation, the id and deleted set to True.
Beispiel
from openemail import openemail removed = openemail.automations.delete('aut_5c1e9a7b3d2f48e6a0b4c7d1') print(removed['id'], removed['deleted'])Hinweise
An OAuth access token needs a verification code for this call, and is refused with 403
step_up_requireduntil the app has verified one in the last 60 minutes.is_step_up_requiredon the error says so. An API key is never asked for a code.The SDK does not retry a delete. A 404 on your own second attempt after a lost response means the first one worked.
Auch verfügbar über
automations.publish()
Publish the draft and turn the automation on
def publish( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationDetailResourceSaves the draft as the next version and sets the automation live, so its trigger starts taking contacts in. Contacts already in it stay on the version they entered on. Publishing a paused automation turns it back on. It takes no body.
The draft has to be complete: a trigger, at least one step, every email with a published template that declares unsubscribeUrl and a from address the caller may send as, and every audience, form and webhook it names still there. Anything else is 422 invalid_automation, and problems on the error lists every problem that blocks it.
The automation sends as whoever published it. When that API key is revoked, expires or is turned off, the automation pauses itself with pausedReason key_revoked.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationDetailResource with status live, a new publishedAt, the publishedVersion that is now running and hasUnpublishedChanges set to False.
Beispiel
from openemail import openemail automation = openemail.automations.publish('aut_5c1e9a7b3d2f48e6a0b4c7d1') print(automation['status'], automation['publishedVersion'], automation['publishedAt'])Hinweise
A plan covers a number of live automations: 1 on Free, 10 on Starter and 50 on Business, with no limit on Enterprise. One more is 403
automation_limit_reached. Pause one, or upgrade.An archived automation answers 409
automation_archived.Publishing a draft that has not changed since the last publish keeps the same version number.
Retried automatically on network failure and retryable statuses, since publishing the same draft twice leaves the same live version.
Auch verfügbar über
automations.pause()
Stop a live automation without losing anybody
def pause( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationDetailResourceStops a live automation. Nobody new enters, and everyone in it stays where they are and moves on when it is resumed. The automation.paused webhook event fires. Pausing a paused automation changes nothing. It takes no body.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationDetailResource with status paused, pausedReason manual and pausedAt set.
Beispiel
from openemail import openemail automation = openemail.automations.pause('aut_5c1e9a7b3d2f48e6a0b4c7d1') print(automation['status'], automation['pausedReason'], automation['counts']['active'])Hinweise
A draft that was never published answers 409
automation_not_published, and an archived automation 409automation_archived.Retried automatically on network failure and retryable statuses, since pausing twice leaves the same paused automation.
Auch verfügbar über
automations.resume()
Turn a paused automation back on
def resume( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationDetailResourceTurns a paused automation back on with the version it was running, without publishing the draft. Contacts that were held move on, and from now on it sends as the caller. Resuming a live automation changes nothing. It takes no body.
The published version is checked again first, exactly as publish checks a draft. An automation OpenEmail paused, because its from address, its templates or an audience went away, stays paused with 422 invalid_automation until what stopped it is fixed.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationDetailResource with status live and pausedReason, pausedAt and lastError cleared.
Beispiel
from openemail import openemail automation = openemail.automations.resume('aut_5c1e9a7b3d2f48e6a0b4c7d1') print(automation['status'], automation['publishedVersion'])Hinweise
An automation with no published version answers 409
automation_not_published, an archived one 409automation_archived, and a plan that covers no more live automations 403automation_limit_reached.Retried automatically on network failure and retryable statuses, since resuming twice leaves the same live automation.
Auch verfügbar über
automations.archive()
Retire an automation for good and keep its history
def archive( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationDetailResourceRetires an automation. Everyone in it leaves with the exit reason archived, nobody enters again, and it can no longer be changed, published or resumed. Its versions, enrollments and statistics stay readable. Archiving an archived automation changes nothing. It takes no body.
An automation with many contacts in it empties in the background over the next minutes. To start again from its steps, use duplicate.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationDetailResource with status archived and archivedAt set.
Beispiel
from openemail import openemail automation = openemail.automations.archive('aut_5c1e9a7b3d2f48e6a0b4c7d1') print(automation['status'], automation['archivedAt'])Hinweise
There is no way back from archived. To stop an automation for a while, use
pause.Retried automatically on network failure and retryable statuses, since archiving twice leaves the same archived automation.
Auch verfügbar über
automations.duplicate()
Copy an automation into a new draft
def duplicate( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationDetailResourceMakes a new draft with the same draft definition and settings, named after the original with (copy) on the end. The copy has no versions, no contacts and no statistics, and it works on an archived automation too. It takes no body.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationDetailResource for the copy, with its own id and status draft.
Beispiel
from openemail import openemail copy = openemail.automations.duplicate('aut_5c1e9a7b3d2f48e6a0b4c7d1') print(copy['id'], copy['name'], copy['status'])Hinweise
A workspace that already holds the most automations it may answers 422
workspace_limit_reached.Not retried automatically, so a retry after a lost response can leave two copies. List them and delete the spare.
Auch verfügbar über
automations.send_test()
Send yourself the email of one step
def send_test( id: str, body: AutomationTestSend, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationTestResourceSends the email of one step of the draft to one address, so you can read it before publishing. Contact values are filled from a sample contact, values that come from an event are left empty, and the subject starts with [Test]. It counts toward the monthly sends, is not tracked and enrolls nobody.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.body['stepKey']strErforderlichThe
keyof the email step to send, from the draftdefinition.body['to']strWhere the test goes. Left out, it goes to the account email of the person the key or app acts for: the workspace owner for an API key.
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationTestResource with automationId, emailId, the email as emails.get returns it, to and stepKey.
Beispiel
from openemail import openemail test = openemail.automations.send_test( 'aut_5c1e9a7b3d2f48e6a0b4c7d1', {'stepKey': 'welcome', 'to': '[email protected]'},) print(test['emailId'], test['to'])Hinweise
A
stepKeythat names no step, a step that is not an email, or a template that cannot be sent is 422invalid_automationwithparamstepKey.The from address of the step has to be one the caller may send as, or the call is refused with 403
from_address_forbidden. A domain that cannot send yet is 409domain_not_sendable, and a plan with no sends left is 429send_quota_exceeded.Not retried automatically, because a retry after a lost response would send the test twice.
Auch verfügbar über
automations.list_versions()
List the published versions of an automation
def list_versions( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> builtins.list[AutomationVersionResource]Returns every published version, the newest first, each with its whole definition. current marks the one that is running. A publish that changed the definition adds a version, and each contact stays on the version they entered on. A draft that was never published has none. The list is not paginated.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
list[AutomationVersionResource], each with id, automationId, version, definition, current, createdBy and createdAt.
Beispiel
from openemail import openemail versions = openemail.automations.list_versions('aut_5c1e9a7b3d2f48e6a0b4c7d1') for version in versions: print(version['version'], version['current'], version['createdAt'])Hinweise
Read only, so the SDK retries it after a network failure like any other read.
Auch verfügbar über
automations.restore_version()
Copy an earlier version back into the draft
def restore_version( id: str, version: int, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationDetailResourceCopies the definition of an earlier version into the draft, replacing what the draft holds. Nothing that is running changes until you publish. It takes no body.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.versionintErforderlichThe number of the version, from
list_versions. A whole number from 1 up.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationDetailResource with the version in definition, and hasUnpublishedChanges set to True when it differs from the one that is running.
Beispiel
from openemail import openemail automation = openemail.automations.restore_version('aut_5c1e9a7b3d2f48e6a0b4c7d1', 2) print(automation['hasUnpublishedChanges'], len(automation['definition']['steps']))Hinweise
A version the automation does not have is 404
automation_not_foundwithparamversion, and an archived automation is 409automation_archived.Retried automatically on network failure and retryable statuses, since restoring the same version twice leaves the same draft.
Auch verfügbar über
automations.stats()
Read how an automation has performed
def stats( id: str, *, since: datetime | str | None = None, until: datetime | str | None = None, api_key: str | None = None, timeout: float | None = None,) -> AutomationStatsResourceReturns the numbers of an automation over a window: totals for the whole automation, steps with the same numbers step by step, and series with a point for each UTC day on which something happened. The window defaults to the last 30 days and reaches back at most 366 days before until=.
The steps are those of the published version, or of the draft when nothing is published. active in totals and the waiting of each step are counted at the moment of the read, whatever the window. Opens are a floor, because many mail apps hide them.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.sincedatetime | strWhere the window starts, as a
datetimeor an ISO 8601 string. Left out, 30 days beforeuntil=.untildatetime | strWhere the window ends, as a
datetimeor an ISO 8601 string. Left out, now. It has to be later thansince=.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationStatsResource with automationId, since, until, totals (entered, active, completed, exited, sent, delivered, opened, clicked, bounced, complained and unsubscribed), steps and series.
Beispiel
from openemail import openemail stats = openemail.automations.stats( 'aut_5c1e9a7b3d2f48e6a0b4c7d1', since='2026-09-01T00:00:00Z')totals = stats['totals'] print(totals['entered'], totals['sent'], totals['clicked']) for step in stats['steps']: print(step['stepKey'], step['kind'], step['entered'], step['waiting'])Hinweise
A time that is not an ISO 8601 instant, or an
until=that is not aftersince=, is 422invalid_parameter.Read only, so the SDK retries it after a network failure like any other read.
Auch verfügbar über
automations.list_enrollments()
List one page of the contacts in an automation
def list_enrollments( id: str, *, limit: int | None = None, cursor: str | None = None, status: AutomationEnrollmentStatus | None = None, step_key: str | None = None, q: str | None = None, api_key: str | None = None, timeout: float | None = None,) -> Page[AutomationEnrollmentResource]Returns one page of everyone who is in the automation or has been, the most recent entry first. Each enrollment says who the contact is, the stepKey they are at, whether they are waiting and for which event, what holds a step that is due in heldFor, when they move next in nextRunAt, and how it ended in exitReason.
limit= takes 1 to 200 and defaults to 50. Pass nextCursor back as cursor=, with the same filters, while hasMore is True. list_all_enrollments and iterate_enrollments do that walk for you.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.limitintRows per page, a whole number from 1 to 200. The server defaults to 50.
cursorstrThe
nextCursorfrom the previous page, passed back exactly as it came. One that names no enrollment of this automation is a 400invalid_cursor.statusAutomationEnrollmentStatusOnly enrollments in this state:
active,completedorexited.AUTOMATION_ENROLLMENT_STATUSESnames them.step_keystrOnly contacts at the step with this
key.qstrOnly contacts whose address or name contains this text, compared without case, up to 200 characters.
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
Page[AutomationEnrollmentResource], a dict with items, hasMore and nextCursor. Each item has id, automationId, version, contact, status, source, stepKey, waiting, waitingForEvent, heldFor, nextRunAt, exitReason, lastError, startedAt, finishedAt and updatedAt.
Beispiel
from openemail import openemail page = openemail.automations.list_enrollments('aut_5c1e9a7b3d2f48e6a0b4c7d1', status='active') for enrollment in page['items']: print(enrollment['contact']['email'], enrollment['stepKey'], enrollment['nextRunAt'])Hinweise
Read only, so the SDK retries it after a network failure like any other read.
Auch verfügbar über
automations.list_all_enrollments()
Collect every enrollment of an automation into one list
def list_all_enrollments( id: str, *, limit: int | None = None, cursor: str | None = None, status: AutomationEnrollmentStatus | None = None, step_key: str | None = None, q: str | None = None, api_key: str | None = None, timeout: float | None = None,) -> builtins.list[AutomationEnrollmentResource]Follows nextCursor from page to page and returns every enrollment that matches as one list, the most recent entry first, in the shape list_enrollments returns. limit= sets the page size of each request, not the total. An automation can hold a great many enrollments, so prefer iterate_enrollments when you do not need them all in memory.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.limitintPage size per request, from 1 to 200, defaulting to 50 on the server.
cursorstrA
nextCursorfrom an earlier page to start after.statusAutomationEnrollmentStatusOnly enrollments in this state:
active,completedorexited.step_keystrOnly contacts at the step with this
key.qstrOnly contacts whose address or name contains this text, compared without case, up to 200 characters.
api_keystrOverrides the client's API key for every page of this walk.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
list[AutomationEnrollmentResource] holding every matching enrollment across all pages.
Beispiel
from openemail import openemail exited = openemail.automations.list_all_enrollments( 'aut_5c1e9a7b3d2f48e6a0b4c7d1', status='exited', limit=200) unsubscribed = [ enrollment for enrollment in exited if enrollment['exitReason'] == 'unsubscribed'] print(f'{len(unsubscribed)} of {len(exited)} left by unsubscribing')Hinweise
A failure on any page raises, and the enrollments already fetched are discarded.
Auch verfügbar über
automations.iterate_enrollments()
Stream the enrollments of an automation one at a time
def iterate_enrollments( id: str, *, limit: int | None = None, cursor: str | None = None, status: AutomationEnrollmentStatus | None = None, step_key: str | None = None, q: str | None = None, api_key: str | None = None, timeout: float | None = None,) -> Iterator[AutomationEnrollmentResource]Returns a generator that yields enrollments one by one, the most recent entry first, and requests the next page only once the current one is used up. Breaking out of the loop stops the requests.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.limitintPage size per request, from 1 to 200, defaulting to 50 on the server.
cursorstrA
nextCursorfrom an earlier page to start after.statusAutomationEnrollmentStatusOnly enrollments in this state:
active,completedorexited.step_keystrOnly contacts at the step with this
key.qstrOnly contacts whose address or name contains this text, compared without case, up to 200 characters.
api_keystrOverrides the client's API key for every page of this walk.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
Iterator[AutomationEnrollmentResource], a generator yielding one enrollment per step.
Beispiel
from openemail import openemail enrollments = openemail.automations.iterate_enrollments( 'aut_5c1e9a7b3d2f48e6a0b4c7d1', status='active') for enrollment in enrollments: if enrollment['heldFor'] is not None: print(enrollment['contact']['email'], 'held for', enrollment['heldFor'])Hinweise
The generator is lazy, so an abandoned loop costs only the pages it read.
Auch verfügbar über
automations.get_enrollment()
Retrieve one contact's way through an automation
def get_enrollment( id: str, enrollment_id: str, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationEnrollmentDetailResourceReturns one enrollment with runs: what each step did for the contact, oldest first, up to 200. A run has the stepKey, the kind of step, an outcome such as sent, waited, yes, no, skipped or failed, the emailId a send step produced and a detail that says why a step was skipped or failed.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.enrollment_idstrErforderlichEnrollment id such as
aen_2b8d4f6a1c3e5079b6d8f0a2, fromlist_enrollments.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationEnrollmentDetailResource: id, automationId, version, contact, status, source, stepKey, waiting, waitingForEvent, heldFor, nextRunAt, exitReason, lastError, startedAt, finishedAt and updatedAt, plus runs.
Beispiel
from openemail import openemail enrollment = openemail.automations.get_enrollment( 'aut_5c1e9a7b3d2f48e6a0b4c7d1', 'aen_2b8d4f6a1c3e5079b6d8f0a2') for run in enrollment['runs']: print(run['createdAt'], run['stepKey'], run['outcome'], run['detail'])Hinweise
An enrollment id that is not in this automation is 404
automation_enrollment_not_found.Read only, so the SDK retries it after a network failure like any other read.
Auch verfügbar über
automations.enroll()
Put a contact into a live automation
def enroll( id: str, body: AutomationEnroll, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationEnrollmentResourcePuts one contact into a live automation at its first step, whatever its trigger is. Name the contact with email or contactId, never both. The contact has to exist already: save one with contacts.create first. data gives the steps the values they would otherwise read from an event.
A contact is in an automation once at a time, and comes back in only after the reentryDays of its settings. An address on the suppression list, or one that unsubscribed from the audience the automation sends through, is refused.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.body['email']strThe contact, by email address. Send this or
contactId.body['contactId']strThe contact, by id, as an event or another enrollment carries it. Send this or
email.body['data']dict[str, Any]Values the steps can read wherever a value comes from the event, such as an order number for an email. At most 50 keys and 4 KB of JSON.
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationEnrollmentResource with status active, source api and nextRunAt now. The first step runs within about a minute.
Beispiel
from openemail import openemail enrollment = openemail.automations.enroll( 'aut_5c1e9a7b3d2f48e6a0b4c7d1', {'email': '[email protected]', 'data': {'plan': 'team'}},) print(enrollment['id'], enrollment['status'], enrollment['nextRunAt'])Hinweise
An automation that is a draft, paused or archived answers 409
automation_not_live. A contact who is in it, or finished it too recently, answers 409already_enrolled, and a suppressed or unsubscribed address 409contact_unreachable. An address or id nobody has is 404contact_not_found.Sending neither or both of
emailandcontactIdis 422invalid_parameter.Not retried automatically. After a lost response, a second attempt that answers 409
already_enrolledmeans the first one worked.
Auch verfügbar über
automations.exit_enrollment()
Take a contact out of an automation
def exit_enrollment( id: str, enrollment_id: str, *, api_key: str | None = None, timeout: float | None = None,) -> AutomationEnrollmentResourceEnds an active enrollment at once with the exit reason removed. The contact gets nothing more from this automation, and stays in the contacts and in their audiences. It takes no body.
Parameter
idstrErforderlichAutomation id such as
aut_5c1e9a7b3d2f48e6a0b4c7d1.enrollment_idstrErforderlichEnrollment id such as
aen_2b8d4f6a1c3e5079b6d8f0a2, fromlist_enrollments.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Rückgabe
AutomationEnrollmentResource with status exited, exitReason removed and finishedAt set.
Beispiel
from openemail import openemail enrollment = openemail.automations.exit_enrollment( 'aut_5c1e9a7b3d2f48e6a0b4c7d1', 'aen_2b8d4f6a1c3e5079b6d8f0a2') print(enrollment['status'], enrollment['exitReason'], enrollment['finishedAt'])Hinweise
An enrollment that already completed or left answers 409
automation_enrollment_finished.Not retried automatically. After a lost response, a second attempt that answers 409
automation_enrollment_finishedmeans the first one worked.