---
title: Loops transactional templates
description: Configure a Loops account and send a job that uses a published transactional template.
---

Use `LoopsTransactionalEmail` from `@corven/email-sdk/loops`. This is a native published-template service, not a literal-message `Email` Layer or ordinary routing target.

```ts
import type { Redacted } from "effect";
import { Effect, Layer } from "effect";
import { FetchHttpClient } from "effect/http";
import { LoopsTransactionalEmail, loopsTransactionalLayer } from "@corven/email-sdk/loops";

export const syntheticLoopsNotification = Effect.gen(function* submitNativeLoopsTemplate() {
  const loops = yield* LoopsTransactionalEmail;

  return yield* loops.send(
    {
      dataVariables: { invoiceNumber: "fictional-invoice", total: 0 },
      email: "recipient@fictional.invalid",
      transactionalId: "fictional_published_template",
    },
    { idempotencyKey: "fictional-invoice-notification" },
  );
});

export const syntheticLoopsNotificationBatch = Effect.gen(function* submitNativeLoopsBatch() {
  const loops = yield* LoopsTransactionalEmail;

  return yield* loops.sendAll(
    [
      {
        job: {
          dataVariables: { invoiceNumber: "fictional-first", total: 0 },
          email: "first@fictional.invalid",
          transactionalId: "fictional_published_template",
        },
        options: { idempotencyKey: "fictional-first-notification" },
      },
      {
        job: {
          dataVariables: { invoiceNumber: "fictional-second", total: 2 },
          email: "second@fictional.invalid",
          transactionalId: "fictional_published_template",
        },
        options: { idempotencyKey: "fictional-second-notification" },
      },
    ],
    { concurrency: 2 },
  );
});

export const loopsNotificationLayer = (apiKey: Redacted.Redacted) =>
  loopsTransactionalLayer({
    apiKey,
    attachments: "Disabled",
    templateAuthority: "ProviderManaged",
  }).pipe(
    Layer.provide(
      FetchHttpClient.layer.pipe(
        Layer.provide(Layer.succeed(FetchHttpClient.RequestInit, { redirect: "manual" })),
      ),
    ),
  );
```

## Configure the template

Supply `apiKey`, `templateAuthority: "ProviderManaged"` and an attachment policy of Disabled or AccountEnabled. These options authorize provider-owned content and describe known account prerequisites; the SDK does not publish or inspect a template.

## Build a job

`send` takes `transactionalId`, one plain `email` recipient and optional `dataVariables`/attachments. Variables support bounded strings, finite numbers and bounded nonempty arrays of flat records. Arbitrary nested application JSON and booleans are not interchangeable with this input contract.

Attachments require explicit MIME types, no inline CID placement and account enablement. Common sender/subject/body fields are not a native template job.

`validate` returns an owned job without sending. `sendAll` takes `{ job, options? }` envelopes, returning ordered per-item Results with bounded concurrency. Defects and interruption still propagate.

## Handle acknowledgment and keys

An accepted Loops receipt can omit the provider message ID. Store the receipt without adding an ID. The service forwards keys of up to 100 characters and declares `KeyForwarding`. The SDK does not retry requests with unknown submission outcomes.

For shared variables or instrumentation, use [native defaults](/composition/native-defaults) and [native observations/capture](/composition/native-instrumentation).
