API Integration Guide
This guide explains how to connect your application to Correios via the API. It covers authentication, syncing contacts, firing events, and the patterns that make integrations reliable and maintainable.
Authentication
Every API request requires two headers:
Get your API key from Settings > API Keys. Your project ID is shown on the Projects page.
Core Operations
Sync a Contact
Call this whenever user data changes in your app. It creates the contact if they don't exist, or updates them if they do.
Fire an Event
Call this when something happens that should trigger a flow.
Properties passed in the event payload are stored on the contact's custom fields (if those fields exist). So {{custom.dashboardUrl}} in a flow email will resolve correctly even if the contact was just created.
Forget a Contact When an Account Is Deleted
When a user deletes their account, or asks you to delete it, call POST /contacts/forget. It stops every flow the contact is enrolled in and archives them, so later events or syncs can never enroll or email them again.
The deletion confirmation email lives in Mailtani, not in your app. Build a flow with the Event Received trigger set to contact.forgotten. With send_farewell: true, Mailtani fires that event after archiving, and that flow is the only kind allowed to email the archived contact.
Call it after your own deletion succeeds, so a failed deletion doesn't leave a live user who has been silenced. See Forget a Contact for the full reference.
The vitelnk Integration Pattern
vitelnk uses a three-layer integration pattern that handles all use cases cleanly:
Layer 1: Sync on Every Change
A MailtaniService::createOrUpdateContact() call is made whenever the user's data changes — on registration, subscription, upload, settings change. This keeps custom field values fresh in Correios.
The contact sync includes all relevant fields: plan, trial status, video count, total views, dashboard URL, etc.
Layer 2: Fire Events from App Logic
Events are fired from the specific code paths where the action happens:
Layer 3: Cron Jobs for Time-Based Events
Time-based events can't be fired from app code — they need to run on a schedule. The pattern is:
- Write an Artisan command that queries your DB for contacts that qualify
- Fire the event for each qualifying contact
- Record in a
sent_marketing_emailstable to prevent double-sends - Schedule the command to run on the appropriate interval
Schedule in routes/console.php or app/Console/Kernel.php:
Custom Fields
Custom fields in Correios store per-contact data. Before syncing values, the fields must exist in your project.
Create a custom field (via dashboard or API):
This endpoint is idempotent — calling it again with the same name won't create a duplicate. Safe to call on every deploy.
Field types: text, number, boolean, date
Sync values via contact upsert:
All values are stored as strings internally. Date fields should be sent as ISO 8601 (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ). Boolean fields as "true" or "false". Number fields as string representation of the number.
Deduplication
Correios prevents a contact from being in the same flow twice simultaneously, but it doesn't prevent you from firing the same event repeatedly. If your cron fires trial.expiring every hour and a user qualifies for 2 hours, they'll get enrolled twice.
Solution: maintain a sent_marketing_emails table in your app.
Before firing an event:
After firing:
This prevents duplicate sends regardless of how many times the cron runs.
Event Naming Conventions
Use noun.verb format, all lowercase, dot-separated:
Properties as state: When you want to branch in a flow based on a contact's state, pass it as a property rather than creating separate events:
Then in Correios: trial.no_upload trigger → Audience Filter nudgeDay = 7 → send the right email.
This is better than creating trial.no_upload_day_7 and trial.no_upload_day_12 as separate events.
PHP / Laravel Code Examples
MailtaniService wrapper
Enabling for local testing
In config/services.php:
Set MAILTANI_TESTING=true in your local .env to enable Correios integration without changing APP_ENV.
