sendandretain 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sendandretain-0.2.0/.gitignore +28 -0
- sendandretain-0.2.0/LICENSE +21 -0
- sendandretain-0.2.0/PKG-INFO +267 -0
- sendandretain-0.2.0/README.md +241 -0
- sendandretain-0.2.0/openapi.json +24130 -0
- sendandretain-0.2.0/pyproject.toml +93 -0
- sendandretain-0.2.0/scripts/generate.py +142 -0
- sendandretain-0.2.0/scripts/generate_facade.py +374 -0
- sendandretain-0.2.0/scripts/normalize_spec.py +140 -0
- sendandretain-0.2.0/scripts/verify_schema_sync.py +92 -0
- sendandretain-0.2.0/src/sendandretain/__init__.py +67 -0
- sendandretain-0.2.0/src/sendandretain/_base.py +93 -0
- sendandretain-0.2.0/src/sendandretain/_client.py +112 -0
- sendandretain-0.2.0/src/sendandretain/_convenience.py +76 -0
- sendandretain-0.2.0/src/sendandretain/_exceptions.py +106 -0
- sendandretain-0.2.0/src/sendandretain/_resources.py +984 -0
- sendandretain-0.2.0/src/sendandretain/_runtime.py +295 -0
- sendandretain-0.2.0/src/sendandretain/_version.py +3 -0
- sendandretain-0.2.0/src/sendandretain/_webhooks.py +78 -0
- sendandretain-0.2.0/src/sendandretain/api/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/audience/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/audience/create_segment.py +213 -0
- sendandretain-0.2.0/src/sendandretain/api/audience/delete_segment.py +176 -0
- sendandretain-0.2.0/src/sendandretain/api/audience/list_segments.py +149 -0
- sendandretain-0.2.0/src/sendandretain/api/audience/refresh_segment_count.py +188 -0
- sendandretain-0.2.0/src/sendandretain/api/audience/update_segment.py +202 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/add_automation_steps.py +202 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/archive_automation.py +184 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/backfill_missed_enrollments.py +243 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/create_automation.py +221 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/duplicate_automation.py +211 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/get_automation.py +189 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/get_automation_metrics.py +222 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/list_automation_runs.py +240 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/list_automations.py +157 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/move_automation_step.py +198 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/preflight_automation.py +197 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/promote_ab_winner.py +218 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/remove_automation_step.py +198 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/set_ab_test.py +214 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/set_automation_status.py +227 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/sync_automation_step_props.py +202 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/update_automation.py +218 -0
- sendandretain-0.2.0/src/sendandretain/api/automations/update_automation_step.py +202 -0
- sendandretain-0.2.0/src/sendandretain/api/contacts/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/contacts/delete_contact.py +188 -0
- sendandretain-0.2.0/src/sendandretain/api/contacts/get_contact.py +184 -0
- sendandretain-0.2.0/src/sendandretain/api/contacts/import_contacts.py +221 -0
- sendandretain-0.2.0/src/sendandretain/api/contacts/list_contact_events.py +244 -0
- sendandretain-0.2.0/src/sendandretain/api/contacts/list_contacts.py +233 -0
- sendandretain-0.2.0/src/sendandretain/api/contacts/upsert_contact.py +192 -0
- sendandretain-0.2.0/src/sendandretain/api/emails/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/emails/cancel_email.py +188 -0
- sendandretain-0.2.0/src/sendandretain/api/emails/get_message.py +182 -0
- sendandretain-0.2.0/src/sendandretain/api/emails/list_emails.py +253 -0
- sendandretain-0.2.0/src/sendandretain/api/emails/reschedule_email.py +209 -0
- sendandretain-0.2.0/src/sendandretain/api/emails/send_email.py +273 -0
- sendandretain-0.2.0/src/sendandretain/api/emails/send_email_batch.py +263 -0
- sendandretain-0.2.0/src/sendandretain/api/events/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/events/emit_event.py +224 -0
- sendandretain-0.2.0/src/sendandretain/api/metrics/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/metrics/get_metrics.py +219 -0
- sendandretain-0.2.0/src/sendandretain/api/metrics/get_metrics_trends.py +197 -0
- sendandretain-0.2.0/src/sendandretain/api/metrics/get_queue_health.py +165 -0
- sendandretain-0.2.0/src/sendandretain/api/settings/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/settings/get_brand.py +165 -0
- sendandretain-0.2.0/src/sendandretain/api/settings/get_project_settings.py +161 -0
- sendandretain-0.2.0/src/sendandretain/api/settings/update_brand.py +193 -0
- sendandretain-0.2.0/src/sendandretain/api/settings/update_project_settings.py +209 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/create_domain.py +225 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/create_sender.py +213 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/delete_domain.py +205 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/delete_sender.py +197 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/get_connection_status.py +173 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/get_onboarding_steps.py +181 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/list_domains.py +149 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/list_senders.py +149 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/register_provider_webhook.py +177 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/register_webhooks.py +169 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/update_sender.py +218 -0
- sendandretain-0.2.0/src/sendandretain/api/setup/verify_domain.py +192 -0
- sendandretain-0.2.0/src/sendandretain/api/suppressions/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/suppressions/add_suppression.py +209 -0
- sendandretain-0.2.0/src/sendandretain/api/suppressions/import_suppressions.py +217 -0
- sendandretain-0.2.0/src/sendandretain/api/suppressions/list_suppressions.py +219 -0
- sendandretain-0.2.0/src/sendandretain/api/suppressions/remove_suppression.py +193 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/add_template_translation.py +214 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/archive_template.py +193 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/create_template.py +213 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/get_template.py +210 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/list_template_versions.py +189 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/list_templates.py +157 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/publish_template_version.py +219 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/render_template.py +219 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/send_template_test.py +218 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/unpublish_template.py +205 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/update_template.py +214 -0
- sendandretain-0.2.0/src/sendandretain/api/templates/update_template_meta.py +218 -0
- sendandretain-0.2.0/src/sendandretain/api/webhooks/__init__.py +1 -0
- sendandretain-0.2.0/src/sendandretain/api/webhooks/create_webhook_endpoint.py +225 -0
- sendandretain-0.2.0/src/sendandretain/api/webhooks/delete_webhook_endpoint.py +193 -0
- sendandretain-0.2.0/src/sendandretain/api/webhooks/get_webhook_endpoint.py +181 -0
- sendandretain-0.2.0/src/sendandretain/api/webhooks/list_webhook_deliveries.py +267 -0
- sendandretain-0.2.0/src/sendandretain/api/webhooks/list_webhook_endpoints.py +157 -0
- sendandretain-0.2.0/src/sendandretain/api/webhooks/replay_webhook_delivery.py +216 -0
- sendandretain-0.2.0/src/sendandretain/api/webhooks/rotate_webhook_secret.py +219 -0
- sendandretain-0.2.0/src/sendandretain/api/webhooks/test_webhook_endpoint.py +197 -0
- sendandretain-0.2.0/src/sendandretain/api/webhooks/update_webhook_endpoint.py +214 -0
- sendandretain-0.2.0/src/sendandretain/client.py +268 -0
- sendandretain-0.2.0/src/sendandretain/errors.py +16 -0
- sendandretain-0.2.0/src/sendandretain/models/__init__.py +819 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_body.py +262 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_0.py +146 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_0_props_overrides.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_0_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_1.py +52 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_2.py +114 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_2_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_3.py +140 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_3_attributes.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_3_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_4.py +124 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_4_method.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_4_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_5.py +83 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_6.py +71 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_7.py +712 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_7_branches_filter_variant_0_item.py +96 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_7_branches_filter_variant_0_item_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_7_branches_item.py +310 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_7_filter_variant_0_item.py +96 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_7_filter_variant_0_item_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_7_if_match.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_7_if_no_match.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_0_variant_7_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_1.py +114 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_1_conditions_variant_0.py +92 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_1_conditions_variant_0_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/add_automation_steps_schema_1_op.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/add_suppression_body.py +90 -0
- sendandretain-0.2.0/src/sendandretain/models/add_suppression_body_reason.py +11 -0
- sendandretain-0.2.0/src/sendandretain/models/add_suppression_response_201.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/add_template_translation_body.py +80 -0
- sendandretain-0.2.0/src/sendandretain/models/add_template_translation_response_201.py +96 -0
- sendandretain-0.2.0/src/sendandretain/models/add_template_translation_response_201_status.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/archive_automation_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/archive_template_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/automations_id_ab_test_post_request_body_content_application_json_variants_variant_0_item.py +81 -0
- sendandretain-0.2.0/src/sendandretain/models/automations_id_patch_request_body_content_application_json_trigger_filter_variant_0_variant_0_item.py +96 -0
- sendandretain-0.2.0/src/sendandretain/models/automations_id_patch_request_body_content_application_json_trigger_filter_variant_0_variant_0_item_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/automations_post_request_body_content_application_json_trigger_filter_variant_0_item.py +96 -0
- sendandretain-0.2.0/src/sendandretain/models/automations_post_request_body_content_application_json_trigger_filter_variant_0_item_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/automations_steps_put_request_body_content_application_json_patch_branches_filter_variant_0_item.py +96 -0
- sendandretain-0.2.0/src/sendandretain/models/automations_steps_put_request_body_content_application_json_patch_branches_filter_variant_0_item_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/automations_steps_put_request_body_content_application_json_patch_filter_variant_0_item.py +96 -0
- sendandretain-0.2.0/src/sendandretain/models/automations_steps_put_request_body_content_application_json_patch_filter_variant_0_item_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/backfill_missed_enrollments_body.py +79 -0
- sendandretain-0.2.0/src/sendandretain/models/backfill_missed_enrollments_response_200.py +115 -0
- sendandretain-0.2.0/src/sendandretain/models/cancel_email_response_200.py +78 -0
- sendandretain-0.2.0/src/sendandretain/models/cancel_email_response_200_status.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/contact_result.py +69 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_body.py +378 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_response_201.py +87 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_response_201_status.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_0.py +110 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_0_conditions_variant_0.py +92 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_0_conditions_variant_0_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_0_op.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_0.py +144 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_0_props_overrides.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_0_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_1.py +52 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_2.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_2_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_3.py +136 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_3_attributes.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_3_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_4.py +121 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_4_method.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_4_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_5.py +83 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_6.py +71 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_7.py +703 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_7_branches_filter_variant_0_item.py +96 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_7_branches_filter_variant_0_item_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_7_branches_item.py +313 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_7_filter_variant_0_item.py +96 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_7_filter_variant_0_item_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_7_if_match.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_7_if_no_match.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/create_automation_schema_1_variant_7_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/create_domain_body.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/create_domain_response_201.py +161 -0
- sendandretain-0.2.0/src/sendandretain/models/create_domain_response_201_dmarc.py +110 -0
- sendandretain-0.2.0/src/sendandretain/models/create_domain_response_201_dmarc_recommended_record.py +89 -0
- sendandretain-0.2.0/src/sendandretain/models/create_domain_response_201_dns_records_item.py +99 -0
- sendandretain-0.2.0/src/sendandretain/models/create_domain_response_201_tracking_type_0.py +115 -0
- sendandretain-0.2.0/src/sendandretain/models/create_segment_body.py +105 -0
- sendandretain-0.2.0/src/sendandretain/models/create_segment_body_filter_item.py +81 -0
- sendandretain-0.2.0/src/sendandretain/models/create_segment_body_filter_item_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/create_segment_response_201.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/create_sender_body.py +100 -0
- sendandretain-0.2.0/src/sendandretain/models/create_sender_response_201.py +88 -0
- sendandretain-0.2.0/src/sendandretain/models/create_template_body.py +136 -0
- sendandretain-0.2.0/src/sendandretain/models/create_template_body_category.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/create_template_body_variables_item.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/create_template_body_variables_item_type.py +12 -0
- sendandretain-0.2.0/src/sendandretain/models/create_template_response_201.py +96 -0
- sendandretain-0.2.0/src/sendandretain/models/create_template_response_201_status.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/create_webhook_endpoint_body.py +93 -0
- sendandretain-0.2.0/src/sendandretain/models/create_webhook_endpoint_response_201.py +303 -0
- sendandretain-0.2.0/src/sendandretain/models/delete_contact_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/delete_domain_response_200.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/delete_segment_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/delete_sender_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/delete_webhook_endpoint_response_200.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/duplicate_automation_response_201.py +87 -0
- sendandretain-0.2.0/src/sendandretain/models/duplicate_automation_response_201_status.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/emit_event_body.py +151 -0
- sendandretain-0.2.0/src/sendandretain/models/emit_event_body_contact_attributes.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/emit_event_body_properties.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/emit_event_response_202.py +71 -0
- sendandretain-0.2.0/src/sendandretain/models/emit_event_response_202_status.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/error.py +68 -0
- sendandretain-0.2.0/src/sendandretain/models/error_error.py +143 -0
- sendandretain-0.2.0/src/sendandretain/models/error_error_code.py +24 -0
- sendandretain-0.2.0/src/sendandretain/models/error_error_key_scope.py +10 -0
- sendandretain-0.2.0/src/sendandretain/models/error_error_required_grant.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/error_error_required_scope.py +10 -0
- sendandretain-0.2.0/src/sendandretain/models/event_result.py +121 -0
- sendandretain-0.2.0/src/sendandretain/models/event_result_skipped_item.py +81 -0
- sendandretain-0.2.0/src/sendandretain/models/event_result_skipped_item_reason.py +13 -0
- sendandretain-0.2.0/src/sendandretain/models/get_automation_metrics_response_200.py +152 -0
- sendandretain-0.2.0/src/sendandretain/models/get_automation_metrics_response_200_ab_tests_item.py +132 -0
- sendandretain-0.2.0/src/sendandretain/models/get_automation_metrics_response_200_ab_tests_item_arms_item.py +88 -0
- sendandretain-0.2.0/src/sendandretain/models/get_automation_metrics_response_200_by_step.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_automation_metrics_response_200_by_variant.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_automation_metrics_response_200_run_counts.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_automation_response_200.py +215 -0
- sendandretain-0.2.0/src/sendandretain/models/get_automation_response_200_steps_item.py +203 -0
- sendandretain-0.2.0/src/sendandretain/models/get_automation_response_200_steps_item_config_type_0.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_brand_response_200.py +103 -0
- sendandretain-0.2.0/src/sendandretain/models/get_brand_response_200_brand.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_connection_status_response_200.py +188 -0
- sendandretain-0.2.0/src/sendandretain/models/get_connection_status_response_200_domains_item.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_connection_status_response_200_provider_type_0.py +99 -0
- sendandretain-0.2.0/src/sendandretain/models/get_connection_status_response_200_senders_item.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_contact_response_200.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_metrics_response_200.py +170 -0
- sendandretain-0.2.0/src/sendandretain/models/get_metrics_response_200_by_day.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_metrics_response_200_by_template.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_metrics_response_200_period.py +85 -0
- sendandretain-0.2.0/src/sendandretain/models/get_metrics_response_200_source.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/get_metrics_response_200_totals.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_metrics_trends_response_200.py +106 -0
- sendandretain-0.2.0/src/sendandretain/models/get_metrics_trends_response_200_series_item.py +135 -0
- sendandretain-0.2.0/src/sendandretain/models/get_metrics_trends_response_200_totals.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_onboarding_steps_response_200.py +148 -0
- sendandretain-0.2.0/src/sendandretain/models/get_onboarding_steps_response_200_steps_item.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_project_settings_response_200.py +146 -0
- sendandretain-0.2.0/src/sendandretain/models/get_project_settings_response_200_utm_type_0.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_queue_health_response_200.py +99 -0
- sendandretain-0.2.0/src/sendandretain/models/get_template_response_200.py +189 -0
- sendandretain-0.2.0/src/sendandretain/models/get_template_response_200_variables_item.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/get_webhook_endpoint_response_200.py +294 -0
- sendandretain-0.2.0/src/sendandretain/models/import_contacts_body.py +76 -0
- sendandretain-0.2.0/src/sendandretain/models/import_contacts_body_contacts_item.py +116 -0
- sendandretain-0.2.0/src/sendandretain/models/import_contacts_body_contacts_item_attributes.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/import_contacts_response_200.py +81 -0
- sendandretain-0.2.0/src/sendandretain/models/import_suppressions_body.py +76 -0
- sendandretain-0.2.0/src/sendandretain/models/import_suppressions_body_entries_item.py +89 -0
- sendandretain-0.2.0/src/sendandretain/models/import_suppressions_body_entries_item_reason.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/import_suppressions_response_200.py +81 -0
- sendandretain-0.2.0/src/sendandretain/models/list_automation_runs_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_automation_runs_response_200_data_item.py +305 -0
- sendandretain-0.2.0/src/sendandretain/models/list_automation_runs_response_200_data_item_variant_assignments.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/list_automation_runs_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_automations_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_automations_response_200_data_item.py +106 -0
- sendandretain-0.2.0/src/sendandretain/models/list_automations_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_contact_events_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_contact_events_response_200_data_item.py +149 -0
- sendandretain-0.2.0/src/sendandretain/models/list_contact_events_response_200_data_item_properties.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/list_contact_events_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_contacts_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_contacts_response_200_data_item.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/list_contacts_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_domains_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_domains_response_200_data_item.py +346 -0
- sendandretain-0.2.0/src/sendandretain/models/list_domains_response_200_data_item_dns_records_item.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/list_domains_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_emails_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_emails_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_envelope.py +94 -0
- sendandretain-0.2.0/src/sendandretain/models/list_envelope_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_segments_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_segments_response_200_data_item.py +200 -0
- sendandretain-0.2.0/src/sendandretain/models/list_segments_response_200_data_item_filter_item.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/list_segments_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_senders_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_senders_response_200_data_item.py +154 -0
- sendandretain-0.2.0/src/sendandretain/models/list_senders_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_suppressions_reason.py +12 -0
- sendandretain-0.2.0/src/sendandretain/models/list_suppressions_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_suppressions_response_200_data_item.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/list_suppressions_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_template_versions_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_template_versions_response_200_data_item.py +145 -0
- sendandretain-0.2.0/src/sendandretain/models/list_template_versions_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_templates_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_templates_response_200_data_item.py +154 -0
- sendandretain-0.2.0/src/sendandretain/models/list_templates_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_webhook_deliveries_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_webhook_deliveries_response_200_data_item.py +244 -0
- sendandretain-0.2.0/src/sendandretain/models/list_webhook_deliveries_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/list_webhook_endpoints_response_200.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/list_webhook_endpoints_response_200_data_item.py +294 -0
- sendandretain-0.2.0/src/sendandretain/models/list_webhook_endpoints_response_200_object.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/message_status.py +350 -0
- sendandretain-0.2.0/src/sendandretain/models/message_status_events_item.py +87 -0
- sendandretain-0.2.0/src/sendandretain/models/move_automation_step_body.py +97 -0
- sendandretain-0.2.0/src/sendandretain/models/move_automation_step_body_direction.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/move_automation_step_response_200.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/preflight_automation_response_200.py +88 -0
- sendandretain-0.2.0/src/sendandretain/models/preflight_automation_response_200_checks_item.py +88 -0
- sendandretain-0.2.0/src/sendandretain/models/promote_ab_winner_body.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/promote_ab_winner_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/publish_template_version_body.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/publish_template_version_response_200.py +99 -0
- sendandretain-0.2.0/src/sendandretain/models/refresh_segment_count_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/register_provider_webhook_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/register_webhooks_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/remove_automation_step_body.py +69 -0
- sendandretain-0.2.0/src/sendandretain/models/remove_automation_step_response_200.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/remove_suppression_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/render_template_body.py +84 -0
- sendandretain-0.2.0/src/sendandretain/models/render_template_body_props.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/render_template_response_200.py +120 -0
- sendandretain-0.2.0/src/sendandretain/models/render_template_response_200_props_used.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/replay_webhook_delivery_response_202.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/reschedule_email_body.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/reschedule_email_response_200.py +87 -0
- sendandretain-0.2.0/src/sendandretain/models/reschedule_email_response_200_status.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/rotate_webhook_secret_body.py +63 -0
- sendandretain-0.2.0/src/sendandretain/models/rotate_webhook_secret_response_200.py +78 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_batch_body.py +76 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_batch_body_emails_item.py +282 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_batch_body_emails_item_attachments_item.py +80 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_batch_body_emails_item_headers.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_batch_body_emails_item_props.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_batch_body_emails_item_tags_item.py +69 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_batch_response_202.py +97 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_batch_response_202_data_item.py +110 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_batch_response_202_data_item_error.py +71 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_body.py +279 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_body_attachments_item.py +80 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_body_headers.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_body_props.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/send_email_body_tags_item.py +69 -0
- sendandretain-0.2.0/src/sendandretain/models/send_result.py +82 -0
- sendandretain-0.2.0/src/sendandretain/models/send_result_status.py +10 -0
- sendandretain-0.2.0/src/sendandretain/models/send_template_test_body.py +94 -0
- sendandretain-0.2.0/src/sendandretain/models/send_template_test_body_props.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/send_template_test_response_201.py +79 -0
- sendandretain-0.2.0/src/sendandretain/models/set_ab_test_body.py +115 -0
- sendandretain-0.2.0/src/sendandretain/models/set_ab_test_response_200.py +92 -0
- sendandretain-0.2.0/src/sendandretain/models/set_automation_status_body.py +73 -0
- sendandretain-0.2.0/src/sendandretain/models/set_automation_status_body_status.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/set_automation_status_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/settings_patch_request_body_content_application_json_utm_variant_0.py +79 -0
- sendandretain-0.2.0/src/sendandretain/models/settings_patch_request_body_content_application_json_utm_variant_0_params.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/sync_automation_step_props_body.py +95 -0
- sendandretain-0.2.0/src/sendandretain/models/sync_automation_step_props_body_updates_item.py +79 -0
- sendandretain-0.2.0/src/sendandretain/models/sync_automation_step_props_body_updates_item_props_overrides.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/sync_automation_step_props_response_200.py +72 -0
- sendandretain-0.2.0/src/sendandretain/models/test_webhook_endpoint_response_202.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/unpublish_template_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_body.py +247 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_response_200.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_schema_0.py +110 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_schema_0_conditions_variant_0.py +92 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_schema_0_conditions_variant_0_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_schema_0_op.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_step_body.py +83 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_step_body_patch.py +390 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_step_body_patch_attributes.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_step_body_patch_branches_item.py +134 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_step_body_patch_if_match.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_step_body_patch_if_no_match.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_step_body_patch_method.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_step_body_patch_props_overrides.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_step_body_patch_send_window.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/update_automation_step_response_200.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/update_brand_body.py +68 -0
- sendandretain-0.2.0/src/sendandretain/models/update_brand_body_brand.py +170 -0
- sendandretain-0.2.0/src/sendandretain/models/update_brand_body_brand_colors.py +97 -0
- sendandretain-0.2.0/src/sendandretain/models/update_brand_body_brand_fonts.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/update_brand_body_brand_logo.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/update_brand_body_brand_social_links_item.py +71 -0
- sendandretain-0.2.0/src/sendandretain/models/update_brand_body_brand_social_links_item_platform.py +15 -0
- sendandretain-0.2.0/src/sendandretain/models/update_brand_body_brand_voice.py +72 -0
- sendandretain-0.2.0/src/sendandretain/models/update_brand_response_200.py +83 -0
- sendandretain-0.2.0/src/sendandretain/models/update_brand_response_200_brand.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/update_project_settings_body.py +149 -0
- sendandretain-0.2.0/src/sendandretain/models/update_project_settings_response_200.py +128 -0
- sendandretain-0.2.0/src/sendandretain/models/update_project_settings_response_200_utm_type_0.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/update_segment_body.py +109 -0
- sendandretain-0.2.0/src/sendandretain/models/update_segment_body_filter_item.py +81 -0
- sendandretain-0.2.0/src/sendandretain/models/update_segment_body_filter_item_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/update_segment_response_200.py +61 -0
- sendandretain-0.2.0/src/sendandretain/models/update_sender_body.py +90 -0
- sendandretain-0.2.0/src/sendandretain/models/update_sender_response_200.py +70 -0
- sendandretain-0.2.0/src/sendandretain/models/update_step_schema_0.py +108 -0
- sendandretain-0.2.0/src/sendandretain/models/update_step_schema_0_conditions_variant_0.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/update_step_schema_0_conditions_variant_0_op.py +17 -0
- sendandretain-0.2.0/src/sendandretain/models/update_step_schema_0_op.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/update_template_body.py +132 -0
- sendandretain-0.2.0/src/sendandretain/models/update_template_body_category.py +9 -0
- sendandretain-0.2.0/src/sendandretain/models/update_template_body_variables_item.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/update_template_body_variables_item_type.py +12 -0
- sendandretain-0.2.0/src/sendandretain/models/update_template_meta_body.py +102 -0
- sendandretain-0.2.0/src/sendandretain/models/update_template_meta_response_200.py +81 -0
- sendandretain-0.2.0/src/sendandretain/models/update_template_response_200.py +87 -0
- sendandretain-0.2.0/src/sendandretain/models/update_template_response_200_status.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/update_webhook_endpoint_body.py +102 -0
- sendandretain-0.2.0/src/sendandretain/models/update_webhook_endpoint_response_200.py +294 -0
- sendandretain-0.2.0/src/sendandretain/models/upsert_contact_body.py +107 -0
- sendandretain-0.2.0/src/sendandretain/models/upsert_contact_body_attributes.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/verify_domain_response_200.py +161 -0
- sendandretain-0.2.0/src/sendandretain/models/verify_domain_response_200_dmarc.py +110 -0
- sendandretain-0.2.0/src/sendandretain/models/verify_domain_response_200_dmarc_recommended_record.py +89 -0
- sendandretain-0.2.0/src/sendandretain/models/verify_domain_response_200_dns_records_item.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/verify_domain_response_200_tracking_type_0.py +115 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_bounced.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_bounced_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_bounced_data_detail.py +108 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_bounced_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_bounced_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_canceled.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_canceled_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_canceled_data_detail.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_canceled_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_canceled_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_clicked.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_clicked_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_clicked_data_detail.py +108 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_clicked_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_clicked_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_complained.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_complained_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_complained_data_detail.py +72 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_complained_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_complained_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_delivered.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_delivered_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_delivered_data_detail.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_delivered_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_delivered_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_delivery_delayed.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_delivery_delayed_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_delivery_delayed_data_detail.py +72 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_delivery_delayed_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_delivery_delayed_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_failed.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_failed_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_failed_data_detail.py +90 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_failed_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_failed_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_opened.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_opened_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_opened_data_detail.py +90 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_opened_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_opened_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_sent.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_sent_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_sent_data_detail.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_sent_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_sent_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_suppressed.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_suppressed_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_suppressed_data_detail.py +72 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_suppressed_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_suppressed_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_unsubscribed.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_unsubscribed_data.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_unsubscribed_data_detail.py +72 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_unsubscribed_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_email_unsubscribed_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_webhook_test.py +112 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_webhook_test_data.py +91 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_webhook_test_data_detail.py +47 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_webhook_test_type.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_event_webhook_test_version.py +8 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_message.py +220 -0
- sendandretain-0.2.0/src/sendandretain/models/webhook_message_tags_type_0_item.py +69 -0
- sendandretain-0.2.0/src/sendandretain/py.typed +0 -0
- sendandretain-0.2.0/src/sendandretain/types.py +54 -0
- sendandretain-0.2.0/tests/test_client.py +78 -0
- sendandretain-0.2.0/tests/test_facade.py +306 -0
- sendandretain-0.2.0/tests/test_live.py +34 -0
- sendandretain-0.2.0/tests/test_quickstart.py +55 -0
- sendandretain-0.2.0/tests/test_spec_coverage.py +57 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.eggs/
|
|
8
|
+
|
|
9
|
+
# Environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
|
|
13
|
+
# Tooling caches
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
htmlcov/
|
|
19
|
+
|
|
20
|
+
# uv resolves fresh in CI so a library is tested against current deps
|
|
21
|
+
uv.lock
|
|
22
|
+
|
|
23
|
+
# verify_schema_sync.py generates a throwaway tree here (inside the repo, so
|
|
24
|
+
# that ruff picks up this project's config) and removes it again
|
|
25
|
+
.verify-*/
|
|
26
|
+
|
|
27
|
+
# OS
|
|
28
|
+
.DS_Store
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Trendly Studio LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sendandretain
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: The Send & Retain SDK for Python — send email, sync contacts and verify webhooks, sync or async.
|
|
5
|
+
Project-URL: Homepage, https://sendandretain.com
|
|
6
|
+
Project-URL: Documentation, https://sendandretain.com/docs
|
|
7
|
+
Project-URL: Source, https://github.com/sendandretainhq/sendandretain-py
|
|
8
|
+
Project-URL: Issues, https://github.com/sendandretainhq/sendandretain-py/issues
|
|
9
|
+
Author: Send & Retain
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: automations,email,sendandretain,transactional-email,webhooks
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: attrs>=22.2.0
|
|
24
|
+
Requires-Dist: httpx<0.29.0,>=0.23.1
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# sendandretain
|
|
28
|
+
|
|
29
|
+
The official Python SDK for the [Send & Retain](https://sendandretain.com) email API — send email,
|
|
30
|
+
sync contacts, emit events that drive automations, and verify webhooks. Sync and asyncio.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install sendandretain # or: uv add sendandretain
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Python 3.11 or newer.
|
|
37
|
+
|
|
38
|
+
## Quickstart
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from sendandretain import SendAndRetain
|
|
42
|
+
|
|
43
|
+
client = SendAndRetain() # reads SENDANDRETAIN_API_KEY
|
|
44
|
+
|
|
45
|
+
email = client.emails.send(
|
|
46
|
+
to="jane@acme.com",
|
|
47
|
+
template="welcome",
|
|
48
|
+
props={"firstName": "Jane"},
|
|
49
|
+
)
|
|
50
|
+
print(email.id) # queued — delivery is reported by webhook
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
A send is accepted with `202` and delivered in the background. Follow it with
|
|
54
|
+
`client.emails.get(email.id)` or, better, a [webhook](#webhooks).
|
|
55
|
+
|
|
56
|
+
## Authentication
|
|
57
|
+
|
|
58
|
+
Mint a key in the dashboard. Every Send & Retain API key starts with `aem_`
|
|
59
|
+
and is sent as `Authorization: Bearer aem_…`.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
client = SendAndRetain("aem_...") # or set SENDANDRETAIN_API_KEY and pass nothing
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Scopes and the send grant
|
|
66
|
+
|
|
67
|
+
Authorization has two independent axes.
|
|
68
|
+
|
|
69
|
+
The **scope** is ranked — a key satisfies any requirement at or below its own
|
|
70
|
+
tier:
|
|
71
|
+
|
|
72
|
+
| Scope | What it can do |
|
|
73
|
+
| --- | --- |
|
|
74
|
+
| `read` | See messages, contacts, templates and metrics. Never changes anything, and cannot send. |
|
|
75
|
+
| `write` | Everything `read` does, plus managing contacts, templates, automations, segments and suppressions. |
|
|
76
|
+
| `admin` | Everything `write` does, plus domains, senders and workspace settings. |
|
|
77
|
+
|
|
78
|
+
The **send grant** is a separate boolean, not a rung. Delivering mail to a real
|
|
79
|
+
inbox — `send_email`, `send_email_batch`, `reschedule_email` and
|
|
80
|
+
`send_template_test` — needs `write` **and** the grant; a key can hold `admin`
|
|
81
|
+
and still be refused there. The dashboard mints that combination as
|
|
82
|
+
**Send + manage**; **Manage only** is the same rung with the grant withheld.
|
|
83
|
+
|
|
84
|
+
A key that falls short gets `403`, and the body says which axis refused it:
|
|
85
|
+
`required_scope` / `key_scope` for the tier, or `required_grant` when the tier
|
|
86
|
+
was enough but the key lacks the grant — not a generic denial.
|
|
87
|
+
|
|
88
|
+
## Errors
|
|
89
|
+
|
|
90
|
+
Methods return the parsed model and raise on any failure. Every exception is a
|
|
91
|
+
`SendAndRetainError` carrying `code`, `status`, `request_id` and the full error `body`:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from sendandretain import NotFoundError, QuotaExceededError, SendAndRetainError
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
client.templates.get("welcome")
|
|
98
|
+
except NotFoundError:
|
|
99
|
+
...
|
|
100
|
+
except QuotaExceededError: # 429 daily_cap / monthly_cap — a quota, not a throttle
|
|
101
|
+
...
|
|
102
|
+
except SendAndRetainError as e:
|
|
103
|
+
print(e.code, e.request_id) # quote request_id to support
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Exception | When |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| `InvalidRequestError` | 400 / 422 — fix the request; nothing changed |
|
|
109
|
+
| `AuthenticationError` | 401 — missing, invalid or revoked key |
|
|
110
|
+
| `PermissionDeniedError` | 403 — see `e.body["required_scope"]` / `["required_grant"]` |
|
|
111
|
+
| `NotFoundError` | 404 |
|
|
112
|
+
| `ConflictError` | 409 — an idempotency conflict, or the resource's state forbids it |
|
|
113
|
+
| `RateLimitError` | 429 `rate_limited`, after the SDK's retries |
|
|
114
|
+
| `QuotaExceededError` | 429 `daily_cap` / `monthly_cap` — never retried |
|
|
115
|
+
| `APIError` | anything else, 5xx included |
|
|
116
|
+
| `APIConnectionError` | no response arrived |
|
|
117
|
+
|
|
118
|
+
## Idempotency
|
|
119
|
+
|
|
120
|
+
Pass `idempotency_key` on any create to make it safe to retry:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
client.emails.send(to="jane@acme.com", template="welcome", idempotency_key="welcome/user_42")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The same key and body replays the first result instead of sending again; a different body with the
|
|
127
|
+
same key raises `ConflictError`. Keys live 24 hours. When you pass none, the SDK generates one per
|
|
128
|
+
call, so its own retries can never send twice.
|
|
129
|
+
|
|
130
|
+
## Retries and timeouts
|
|
131
|
+
|
|
132
|
+
`408`, `429` and `5xx` responses, and requests that get no response, are retried twice with
|
|
133
|
+
exponential backoff and jitter, honouring `Retry-After` — but only when safe: reads, updates,
|
|
134
|
+
deletes, and creates carrying an idempotency key (all of them, by default). A sending quota is
|
|
135
|
+
never retried.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
client = SendAndRetain(max_retries=4, timeout=10.0)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Pagination
|
|
142
|
+
|
|
143
|
+
Lists return a page — `.data`, `.has_more`, `.next_cursor`. `iterate` walks every page:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
for email in client.emails.iterate(status="bounced"):
|
|
147
|
+
print(email.to)
|
|
148
|
+
|
|
149
|
+
page = client.contacts.list(limit=100)
|
|
150
|
+
if page.has_more:
|
|
151
|
+
page = client.contacts.list(limit=100, cursor=page.next_cursor)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Under a resource: `client.contacts.iterate_events(contact_id)`,
|
|
155
|
+
`client.automations.iterate_runs(automation_id)`, `client.webhooks.deliveries.iterate(endpoint_id)`.
|
|
156
|
+
|
|
157
|
+
## Async
|
|
158
|
+
|
|
159
|
+
`AsyncSendAndRetain` has the same resources, every method awaitable:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from sendandretain import AsyncSendAndRetain
|
|
163
|
+
|
|
164
|
+
async with AsyncSendAndRetain() as client:
|
|
165
|
+
email = await client.emails.send(to="jane@acme.com", template="welcome")
|
|
166
|
+
async for contact in client.contacts.iterate():
|
|
167
|
+
...
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Webhooks
|
|
171
|
+
|
|
172
|
+
Subscribe an endpoint and store the secret it returns — it is shown once:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
endpoint = client.webhooks.create(
|
|
176
|
+
url="https://acme.com/webhooks/sendandretain",
|
|
177
|
+
event_types=["email.delivered", "email.bounced", "email.complained"],
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Verify every delivery against the **raw** body, then act on `event["type"]`:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from sendandretain import WebhookVerificationError, verify_webhook
|
|
185
|
+
|
|
186
|
+
# Flask: request.get_data() · FastAPI: await request.body() · Django: request.body
|
|
187
|
+
try:
|
|
188
|
+
event = verify_webhook(raw_body, request.headers, secret=os.environ["SENDANDRETAIN_WEBHOOK_SECRET"])
|
|
189
|
+
except WebhookVerificationError:
|
|
190
|
+
return "", 400
|
|
191
|
+
|
|
192
|
+
if event["type"] == "email.bounced":
|
|
193
|
+
...
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Deliveries are [Standard Webhooks](https://www.standardwebhooks.com), at-least-once and unordered:
|
|
197
|
+
dedupe on `event["id"]`, order on `event["occurred_at"]`. Verification is standard library only.
|
|
198
|
+
|
|
199
|
+
## Resources
|
|
200
|
+
|
|
201
|
+
| Resource | Methods |
|
|
202
|
+
| --- | --- |
|
|
203
|
+
| `emails` | `send`, `get`, `list`, `iterate`, `reschedule`, `cancel`, `batch.send` |
|
|
204
|
+
| `contacts` | `upsert`, `get`, `list`, `iterate`, `delete`, `import_`, `events`, `iterate_events` |
|
|
205
|
+
| `events` | `emit` |
|
|
206
|
+
| `suppressions` | `list`, `iterate`, `add`, `remove`, `import_` |
|
|
207
|
+
| `templates` | `create`, `list`, `get`, `update`, `update_meta`, `archive`, `list_versions`, `publish`, `unpublish`, `render`, `test`, `add_translation` |
|
|
208
|
+
| `automations` | `create`, `list`, `get`, `update`, `archive`, `set_status`, `duplicate`, `backfill`, `preflight`, `runs`, `iterate_runs`, `metrics`, `set_ab_test`, `promote_ab_winner`, `steps.{add,update,remove,move,sync_props}` |
|
|
209
|
+
| `segments` | `create`, `list`, `update`, `delete`, `refresh_count` |
|
|
210
|
+
| `domains` | `create`, `list`, `verify`, `delete` |
|
|
211
|
+
| `senders` | `create`, `list`, `update`, `delete` |
|
|
212
|
+
| `webhooks` | `create`, `list`, `get`, `update`, `delete`, `rotate_secret`, `test`, `deliveries.list`, `deliveries.iterate`, `deliveries.replay` |
|
|
213
|
+
| `metrics` | `get`, `trends`, `queue_health` |
|
|
214
|
+
| `settings` | `get`, `update`, `get_brand`, `update_brand` |
|
|
215
|
+
| `setup` | `onboarding`, `connection`, `register_provider_webhook` |
|
|
216
|
+
|
|
217
|
+
Every API operation has a method; a test fails if one is missing. Body fields are keyword arguments
|
|
218
|
+
with their wire names, except `from`, which is `from_`.
|
|
219
|
+
|
|
220
|
+
## How it is organised
|
|
221
|
+
|
|
222
|
+
| Path | What it is |
|
|
223
|
+
| --- | --- |
|
|
224
|
+
| `SendAndRetain`, `AsyncSendAndRetain` | `_client.py`, hand-written. Key, base URL, transport. |
|
|
225
|
+
| resource methods | `_resources.py`, **generated** from `openapi.json` by `scripts/generate_facade.py`. |
|
|
226
|
+
| `sendandretain.api.*`, `sendandretain.models` | **Generated** by openapi-python-client `0.29.0`: one module per operation, attrs models. Use them directly for anything the facade does not shape your way; `client.raw` is the `AuthenticatedClient` they take. |
|
|
227
|
+
| `_runtime.py` | The transport — retries, idempotency, User-Agent. Synced from the Send & Retain source tree. |
|
|
228
|
+
| `_exceptions.py`, `_webhooks.py`, `_base.py` | Hand-written. |
|
|
229
|
+
| `openapi.json` | The vendored spec everything is generated from. |
|
|
230
|
+
|
|
231
|
+
`sendandretain.Client` — the 0.1 factory returning the generated `AuthenticatedClient` — still works.
|
|
232
|
+
|
|
233
|
+
## Regenerating
|
|
234
|
+
|
|
235
|
+
The generated tree and the facade are committed. Regenerate both after changing `openapi.json`:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
uv run python scripts/generate.py
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
And check both are in sync — this is what CI runs:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
uv run python scripts/verify_schema_sync.py
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`scripts/normalize_spec.py` runs first and rewrites two constructs the generator
|
|
248
|
+
cannot model, without changing the contract: anonymous `object`/`array` members
|
|
249
|
+
of an `anyOf`/`oneOf` are lifted into named schemas and replaced with a `$ref`,
|
|
250
|
+
and a `type` list whose `array` branch carries no `items` is widened to an
|
|
251
|
+
unconstrained schema. Both are deterministic, which is what makes the drift
|
|
252
|
+
check meaningful.
|
|
253
|
+
|
|
254
|
+
## Development
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
uv sync
|
|
258
|
+
uv run ruff check .
|
|
259
|
+
uv run ruff format --check .
|
|
260
|
+
uv run mypy
|
|
261
|
+
uv run python scripts/verify_schema_sync.py
|
|
262
|
+
uv run pytest
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# sendandretain
|
|
2
|
+
|
|
3
|
+
The official Python SDK for the [Send & Retain](https://sendandretain.com) email API — send email,
|
|
4
|
+
sync contacts, emit events that drive automations, and verify webhooks. Sync and asyncio.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install sendandretain # or: uv add sendandretain
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Python 3.11 or newer.
|
|
11
|
+
|
|
12
|
+
## Quickstart
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from sendandretain import SendAndRetain
|
|
16
|
+
|
|
17
|
+
client = SendAndRetain() # reads SENDANDRETAIN_API_KEY
|
|
18
|
+
|
|
19
|
+
email = client.emails.send(
|
|
20
|
+
to="jane@acme.com",
|
|
21
|
+
template="welcome",
|
|
22
|
+
props={"firstName": "Jane"},
|
|
23
|
+
)
|
|
24
|
+
print(email.id) # queued — delivery is reported by webhook
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A send is accepted with `202` and delivered in the background. Follow it with
|
|
28
|
+
`client.emails.get(email.id)` or, better, a [webhook](#webhooks).
|
|
29
|
+
|
|
30
|
+
## Authentication
|
|
31
|
+
|
|
32
|
+
Mint a key in the dashboard. Every Send & Retain API key starts with `aem_`
|
|
33
|
+
and is sent as `Authorization: Bearer aem_…`.
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
client = SendAndRetain("aem_...") # or set SENDANDRETAIN_API_KEY and pass nothing
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Scopes and the send grant
|
|
40
|
+
|
|
41
|
+
Authorization has two independent axes.
|
|
42
|
+
|
|
43
|
+
The **scope** is ranked — a key satisfies any requirement at or below its own
|
|
44
|
+
tier:
|
|
45
|
+
|
|
46
|
+
| Scope | What it can do |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `read` | See messages, contacts, templates and metrics. Never changes anything, and cannot send. |
|
|
49
|
+
| `write` | Everything `read` does, plus managing contacts, templates, automations, segments and suppressions. |
|
|
50
|
+
| `admin` | Everything `write` does, plus domains, senders and workspace settings. |
|
|
51
|
+
|
|
52
|
+
The **send grant** is a separate boolean, not a rung. Delivering mail to a real
|
|
53
|
+
inbox — `send_email`, `send_email_batch`, `reschedule_email` and
|
|
54
|
+
`send_template_test` — needs `write` **and** the grant; a key can hold `admin`
|
|
55
|
+
and still be refused there. The dashboard mints that combination as
|
|
56
|
+
**Send + manage**; **Manage only** is the same rung with the grant withheld.
|
|
57
|
+
|
|
58
|
+
A key that falls short gets `403`, and the body says which axis refused it:
|
|
59
|
+
`required_scope` / `key_scope` for the tier, or `required_grant` when the tier
|
|
60
|
+
was enough but the key lacks the grant — not a generic denial.
|
|
61
|
+
|
|
62
|
+
## Errors
|
|
63
|
+
|
|
64
|
+
Methods return the parsed model and raise on any failure. Every exception is a
|
|
65
|
+
`SendAndRetainError` carrying `code`, `status`, `request_id` and the full error `body`:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from sendandretain import NotFoundError, QuotaExceededError, SendAndRetainError
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
client.templates.get("welcome")
|
|
72
|
+
except NotFoundError:
|
|
73
|
+
...
|
|
74
|
+
except QuotaExceededError: # 429 daily_cap / monthly_cap — a quota, not a throttle
|
|
75
|
+
...
|
|
76
|
+
except SendAndRetainError as e:
|
|
77
|
+
print(e.code, e.request_id) # quote request_id to support
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
| Exception | When |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `InvalidRequestError` | 400 / 422 — fix the request; nothing changed |
|
|
83
|
+
| `AuthenticationError` | 401 — missing, invalid or revoked key |
|
|
84
|
+
| `PermissionDeniedError` | 403 — see `e.body["required_scope"]` / `["required_grant"]` |
|
|
85
|
+
| `NotFoundError` | 404 |
|
|
86
|
+
| `ConflictError` | 409 — an idempotency conflict, or the resource's state forbids it |
|
|
87
|
+
| `RateLimitError` | 429 `rate_limited`, after the SDK's retries |
|
|
88
|
+
| `QuotaExceededError` | 429 `daily_cap` / `monthly_cap` — never retried |
|
|
89
|
+
| `APIError` | anything else, 5xx included |
|
|
90
|
+
| `APIConnectionError` | no response arrived |
|
|
91
|
+
|
|
92
|
+
## Idempotency
|
|
93
|
+
|
|
94
|
+
Pass `idempotency_key` on any create to make it safe to retry:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
client.emails.send(to="jane@acme.com", template="welcome", idempotency_key="welcome/user_42")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The same key and body replays the first result instead of sending again; a different body with the
|
|
101
|
+
same key raises `ConflictError`. Keys live 24 hours. When you pass none, the SDK generates one per
|
|
102
|
+
call, so its own retries can never send twice.
|
|
103
|
+
|
|
104
|
+
## Retries and timeouts
|
|
105
|
+
|
|
106
|
+
`408`, `429` and `5xx` responses, and requests that get no response, are retried twice with
|
|
107
|
+
exponential backoff and jitter, honouring `Retry-After` — but only when safe: reads, updates,
|
|
108
|
+
deletes, and creates carrying an idempotency key (all of them, by default). A sending quota is
|
|
109
|
+
never retried.
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
client = SendAndRetain(max_retries=4, timeout=10.0)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Pagination
|
|
116
|
+
|
|
117
|
+
Lists return a page — `.data`, `.has_more`, `.next_cursor`. `iterate` walks every page:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
for email in client.emails.iterate(status="bounced"):
|
|
121
|
+
print(email.to)
|
|
122
|
+
|
|
123
|
+
page = client.contacts.list(limit=100)
|
|
124
|
+
if page.has_more:
|
|
125
|
+
page = client.contacts.list(limit=100, cursor=page.next_cursor)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Under a resource: `client.contacts.iterate_events(contact_id)`,
|
|
129
|
+
`client.automations.iterate_runs(automation_id)`, `client.webhooks.deliveries.iterate(endpoint_id)`.
|
|
130
|
+
|
|
131
|
+
## Async
|
|
132
|
+
|
|
133
|
+
`AsyncSendAndRetain` has the same resources, every method awaitable:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from sendandretain import AsyncSendAndRetain
|
|
137
|
+
|
|
138
|
+
async with AsyncSendAndRetain() as client:
|
|
139
|
+
email = await client.emails.send(to="jane@acme.com", template="welcome")
|
|
140
|
+
async for contact in client.contacts.iterate():
|
|
141
|
+
...
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Webhooks
|
|
145
|
+
|
|
146
|
+
Subscribe an endpoint and store the secret it returns — it is shown once:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
endpoint = client.webhooks.create(
|
|
150
|
+
url="https://acme.com/webhooks/sendandretain",
|
|
151
|
+
event_types=["email.delivered", "email.bounced", "email.complained"],
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Verify every delivery against the **raw** body, then act on `event["type"]`:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from sendandretain import WebhookVerificationError, verify_webhook
|
|
159
|
+
|
|
160
|
+
# Flask: request.get_data() · FastAPI: await request.body() · Django: request.body
|
|
161
|
+
try:
|
|
162
|
+
event = verify_webhook(raw_body, request.headers, secret=os.environ["SENDANDRETAIN_WEBHOOK_SECRET"])
|
|
163
|
+
except WebhookVerificationError:
|
|
164
|
+
return "", 400
|
|
165
|
+
|
|
166
|
+
if event["type"] == "email.bounced":
|
|
167
|
+
...
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Deliveries are [Standard Webhooks](https://www.standardwebhooks.com), at-least-once and unordered:
|
|
171
|
+
dedupe on `event["id"]`, order on `event["occurred_at"]`. Verification is standard library only.
|
|
172
|
+
|
|
173
|
+
## Resources
|
|
174
|
+
|
|
175
|
+
| Resource | Methods |
|
|
176
|
+
| --- | --- |
|
|
177
|
+
| `emails` | `send`, `get`, `list`, `iterate`, `reschedule`, `cancel`, `batch.send` |
|
|
178
|
+
| `contacts` | `upsert`, `get`, `list`, `iterate`, `delete`, `import_`, `events`, `iterate_events` |
|
|
179
|
+
| `events` | `emit` |
|
|
180
|
+
| `suppressions` | `list`, `iterate`, `add`, `remove`, `import_` |
|
|
181
|
+
| `templates` | `create`, `list`, `get`, `update`, `update_meta`, `archive`, `list_versions`, `publish`, `unpublish`, `render`, `test`, `add_translation` |
|
|
182
|
+
| `automations` | `create`, `list`, `get`, `update`, `archive`, `set_status`, `duplicate`, `backfill`, `preflight`, `runs`, `iterate_runs`, `metrics`, `set_ab_test`, `promote_ab_winner`, `steps.{add,update,remove,move,sync_props}` |
|
|
183
|
+
| `segments` | `create`, `list`, `update`, `delete`, `refresh_count` |
|
|
184
|
+
| `domains` | `create`, `list`, `verify`, `delete` |
|
|
185
|
+
| `senders` | `create`, `list`, `update`, `delete` |
|
|
186
|
+
| `webhooks` | `create`, `list`, `get`, `update`, `delete`, `rotate_secret`, `test`, `deliveries.list`, `deliveries.iterate`, `deliveries.replay` |
|
|
187
|
+
| `metrics` | `get`, `trends`, `queue_health` |
|
|
188
|
+
| `settings` | `get`, `update`, `get_brand`, `update_brand` |
|
|
189
|
+
| `setup` | `onboarding`, `connection`, `register_provider_webhook` |
|
|
190
|
+
|
|
191
|
+
Every API operation has a method; a test fails if one is missing. Body fields are keyword arguments
|
|
192
|
+
with their wire names, except `from`, which is `from_`.
|
|
193
|
+
|
|
194
|
+
## How it is organised
|
|
195
|
+
|
|
196
|
+
| Path | What it is |
|
|
197
|
+
| --- | --- |
|
|
198
|
+
| `SendAndRetain`, `AsyncSendAndRetain` | `_client.py`, hand-written. Key, base URL, transport. |
|
|
199
|
+
| resource methods | `_resources.py`, **generated** from `openapi.json` by `scripts/generate_facade.py`. |
|
|
200
|
+
| `sendandretain.api.*`, `sendandretain.models` | **Generated** by openapi-python-client `0.29.0`: one module per operation, attrs models. Use them directly for anything the facade does not shape your way; `client.raw` is the `AuthenticatedClient` they take. |
|
|
201
|
+
| `_runtime.py` | The transport — retries, idempotency, User-Agent. Synced from the Send & Retain source tree. |
|
|
202
|
+
| `_exceptions.py`, `_webhooks.py`, `_base.py` | Hand-written. |
|
|
203
|
+
| `openapi.json` | The vendored spec everything is generated from. |
|
|
204
|
+
|
|
205
|
+
`sendandretain.Client` — the 0.1 factory returning the generated `AuthenticatedClient` — still works.
|
|
206
|
+
|
|
207
|
+
## Regenerating
|
|
208
|
+
|
|
209
|
+
The generated tree and the facade are committed. Regenerate both after changing `openapi.json`:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
uv run python scripts/generate.py
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
And check both are in sync — this is what CI runs:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
uv run python scripts/verify_schema_sync.py
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`scripts/normalize_spec.py` runs first and rewrites two constructs the generator
|
|
222
|
+
cannot model, without changing the contract: anonymous `object`/`array` members
|
|
223
|
+
of an `anyOf`/`oneOf` are lifted into named schemas and replaced with a `$ref`,
|
|
224
|
+
and a `type` list whose `array` branch carries no `items` is widened to an
|
|
225
|
+
unconstrained schema. Both are deterministic, which is what makes the drift
|
|
226
|
+
check meaningful.
|
|
227
|
+
|
|
228
|
+
## Development
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
uv sync
|
|
232
|
+
uv run ruff check .
|
|
233
|
+
uv run ruff format --check .
|
|
234
|
+
uv run mypy
|
|
235
|
+
uv run python scripts/verify_schema_sync.py
|
|
236
|
+
uv run pytest
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
MIT — see [LICENSE](./LICENSE).
|