---
title: "Authentication and API keys - Workflow Transactional Email"
description: "Create, scope and revoke API keys for the Workflow Transactional Email REST API and MCP server. Read, write and execute access levels explained."
canonical: "https://docs.workflow-transactional-email.app/developer-authentication"
---

# Authentication and API keys

Both the REST API and the MCP server use the same credential: an **API key** created on the **Developer** page inside the app.

## Base URL

All requests go to:

```text
https://shopify.workflow-transactional-email.app
```

Every endpoint below is relative to that host, so the full path for the index is `https://shopify.workflow-transactional-email.app/api/v1`.

## Creating a key

Open **Developer** in the app, choose an access level, and create the key. The full value is shown **once, at creation**. Copy it then and store it in your password manager or secret store - it cannot be retrieved afterwards, because only a SHA-256 hash of it is kept.

Keys always start with `fak_`, which makes them easy to spot in a secret scanner.

Send it as a bearer token:

```http
GET /api/v1/me HTTP/1.1
Host: shopify.workflow-transactional-email.app
Authorization: Bearer fak_your_key_here
```

`GET /api/v1/me` is the quickest way to confirm a key works and to see which level it carries.

## Access levels

Levels are **ordered and cumulative**: each one includes everything below it.

| Level | Can do |
| --- | --- |
| **Read** | List and read HTTP requests, email layouts, senders, secret names, history and stats |
| **Read & write** | Everything above, plus create, update and delete HTTP requests |
| **Read, write & execute** | Everything above, plus run a configured HTTP request for real |

### Why execute is separate

Running an action sends a real request to a real third-party system. Keeping that behind its own level means a key you hand to a script - or to an AI assistant - for reading and editing configuration cannot fire requests at your live integrations.

Issue read keys by default. Only add write or execute when a specific job needs them.

> [!WARNING]
> **Secrets are never readable, at any level**
> Secret values, SMTP passwords and OAuth tokens cannot be read back through either surface. Not with a write key, not with an execute key, not through MCP. There is no endpoint and no tool that returns them.
>
> You get `hasValue` / `hasPassword` flags and secret **names** instead, which is enough for an assistant to reference `{{ secrets.stripeApiKey }}` without ever seeing the value. You can always set a new value; you can never retrieve an old one.
>
> The practical consequence: a leaked read key exposes your configuration, not your credentials.

## Revoking a key

Delete the key on the **Developer** page. Revocation takes effect on the next request - there is no cache to wait for.

Revoke and reissue if a key was committed to a repository, pasted into a shared document, or given to a contractor who no longer needs it.

## Good habits

- Reference credentials as `{{ secrets.KEY }}` in your HTTP requests rather than pasting them into a header or body. Literal credentials are masked in API responses, but referencing a secret is better.
- Use one key per consumer, so you can revoke a single integration without breaking the others.
- Keep execute keys off developer laptops and out of AI assistant configurations unless you specifically want the assistant to be able to fire live requests.
