Connect Loops to Fondoo

Live4 min readUpdated

Loops in Fondoo: what your AI agent can do with it, what to set up first, the permissions it needs and its limits.

In Loops open Settings → API and click Generate key (name it for Fondoo); Loops API keys are team-wide with no finer scopes, so use a dedicated key you can revoke. Loops has no API to list all contacts or to send a campaign with HTML, so Fondoo can push subscribers to Loops and trigger events or transactional emails, but cannot import your Loops audience or send Loops campaigns; send those from the Loops dashboard. Transactional sends and events go to one person each; you are responsible for having consent to email that address.

Official documentation: https://loops.so/docs/api-reference/intro

Use add_connection_key without fields for a signed, one-use secure paste page. Then call test_connection and inspect confirmed versus unknown permissions. Direct tool credentials carry a chat-log warning. list_connections never returns credentials.

Auth: api_key (Authorization: Bearer <key>; credential field apiKey, secret). Category: email. Required provider scopes: none (Loops API keys are team-wide and carry no scopes).

Capabilities are discoverable with list_connector_manifests; invoke them through connector_action. Writes retain shared Fondoo scopes, approvals, dry runs, audit, events and metering. Use rotate_connection_key before expiry and delete_connection to remove stored credentials; revoke upstream credentials separately.

What Fondoo does with Loops

Loops is a push-only email platform here: Fondoo can send subscribers, events and transactional emails to Loops, but cannot import your Loops audience or send Loops campaigns (the API has neither). sync_subscribers_from_provider, create_provider_campaign and send_provider_campaign therefore explain that Loops does not support them; sync_subscribers_to_provider works.

Normalized email capabilities:

  • audiences.list (read): GET /lists; each mailing list becomes {id, name}.
  • subscribers.upsert_batch (write): one lookup per member (GET /contacts/find?email=), then:
    • subscribed, not found: POST /contacts/create with source:"Fondoo", optional first/last name split from name, and the mailing list when listId is given. subscribed is never sent, so Loops' default applies (new contacts are subscribed). Outcome created.
    • subscribed, found with subscribed:false or optInStatus:"rejected": skipped_provider_unsubscribed, nothing written. Found and subscribed: only a missing list membership or changed names are written (PUT /contacts/update without subscribed); otherwise unchanged.
    • unsubscribed: only for an existing contact (PUT /contacts/update with subscribed:false, plus the list set to false when listId is given); not found gives unchanged/not_found. Nobody is created just to be unsubscribed.
    • One execute makes at most 25 Loops requests; members beyond that budget come back as outcome:"error", message:"batch_limit" and must be resent. 401/403 and 429 abort the batch; other per-member failures are error rows.

Vendor-native capabilities:

  • contacts.find (read, by exactly one of email / userId), contacts.suppression (read; suppression status and removal quota).
  • contacts.create (write): validated standard fields plus properties (custom property names must start with a letter; reserved names are refused). subscribed may be set only when explicitly supplied.
  • contacts.update (write): looks the contact up first because Loops' update endpoint would otherwise create it. It can set subscribed:false but refuses subscribed:true: re-subscribing must come from the person (a Loops form or dashboard), never an agent.
  • contacts.delete (destructive): POST /contacts/delete; a 404 is reported as deleted:false.
  • mailing_lists.list, contact_properties.list, contact_properties.create (camelCase names, types string/number/boolean/date).
  • events.send (write): sends only for contacts that already exist (Loops would otherwise create a subscribed contact); eventProperties are flat; the Idempotency-Key header is idempotencyKey or the runtime request key. A 409 is reported as sent:false, reason:"duplicate_idempotency_key".
  • transactional.list (read) and transactional.send (write): POST /transactional with transactionalId, email, optional addToAudience and dataVariables (strings, numbers, arrays of flat objects). Attachments are refused. Transactional email is for one person at a time; you remain responsible for having a lawful basis to email that address.

API endpoints used

Limits and uncertainties

  • Rate limit: Loops documents a baseline of 10 requests per second per team; Fondoo budgets 20 executes per minute because one execute may make up to 25 requests. 429 surfaces as rate_limited with Retry-After when sent.
  • Only the find-contact page was re-read while writing this guide (confirming the empty-array response and the subscribed, optInStatus and mailingLists fields); the other endpoint pages are cited from Loops' reference index and were not re-fetched, so verify exact request fields against them.
  • Uncertain: whether PUT /contacts/update with mailingLists:{id:false} unsubscribes from that list only (it does per Loops' mailing-list documentation) and leaves the global subscribed flag unchanged.
  • Uncertain: the idempotency header applies to event and transactional sends only; contact writes are not idempotent, so retries re-run the lookup first.
  • Loops has no member listing API, so provider unsubscribes made inside Loops are only noticed when Fondoo pushes that address again; Fondoo does not poll.