Local drop-in replacement services for CI and no-network sandboxes. Fully stateful, production-fidelity API emulation. Not mocks.
npx emulateAll services start with sensible defaults. No config file needed:
- Vercel on
http://localhost:4000 - GitHub on
http://localhost:4001 - Google on
http://localhost:4002 - Slack on
http://localhost:4003 - Apple on
http://localhost:4004 - Microsoft on
http://localhost:4005 - AWS on
http://localhost:4006
# Start all services (zero-config)
emulate
# Start specific services
emulate --service vercel,github
# Custom port
emulate --port 3000
# Use a seed config file
emulate --seed config.yaml
# Generate a starter config
emulate init
# Generate config for a specific service
emulate init --service vercel
# List available services
emulate list| Flag | Default | Description |
|---|---|---|
-p, --port |
4000 |
Base port (auto-increments per service) |
-s, --service |
all | Comma-separated services to enable |
--seed |
auto-detect | Path to seed config (YAML or JSON) |
The port can also be set via EMULATE_PORT or PORT environment variables.
npm install emulateEach call to createEmulator starts a single service:
import { createEmulator } from 'emulate'
const github = await createEmulator({ service: 'github', port: 4001 })
const vercel = await createEmulator({ service: 'vercel', port: 4002 })
github.url // 'http://localhost:4001'
vercel.url // 'http://localhost:4002'
await github.close()
await vercel.close()// vitest.setup.ts
import { createEmulator, type Emulator } from 'emulate'
let github: Emulator
let vercel: Emulator
beforeAll(async () => {
;[github, vercel] = await Promise.all([
createEmulator({ service: 'github', port: 4001 }),
createEmulator({ service: 'vercel', port: 4002 }),
])
process.env.GITHUB_EMULATOR_URL = github.url
process.env.VERCEL_EMULATOR_URL = vercel.url
})
afterEach(() => { github.reset(); vercel.reset() })
afterAll(() => Promise.all([github.close(), vercel.close()]))| Option | Default | Description |
|---|---|---|
service |
(required) | Service name: 'vercel', 'github', 'google', 'slack', 'apple', 'microsoft', or 'aws' |
port |
4000 |
Port for the HTTP server |
seed |
none | Inline seed data (same shape as YAML config) |
| Method | Description |
|---|---|
url |
Base URL of the running server |
reset() |
Wipe the store and replay seed data |
close() |
Shut down the HTTP server, returns a Promise |
Configuration is optional. The CLI auto-detects config files in this order: emulate.config.yaml / .yml, emulate.config.json, service-emulator.config.yaml / .yml, service-emulator.config.json. Or pass --seed <file> explicitly. Run emulate init to generate a starter file.
tokens:
my_token:
login: admin
scopes: [repo, user]
vercel:
users:
- username: developer
name: Developer
email: dev@example.com
teams:
- slug: my-team
name: My Team
projects:
- name: my-app
team: my-team
framework: nextjs
github:
users:
- login: octocat
name: The Octocat
email: octocat@github.com
orgs:
- login: my-org
name: My Organization
repos:
- owner: octocat
name: hello-world
language: JavaScript
auto_init: true
google:
users:
- email: testuser@example.com
name: Test User
oauth_clients:
- client_id: my-client-id.apps.googleusercontent.com
client_secret: GOCSPX-secret
redirect_uris:
- http://localhost:3000/api/auth/callback/google
labels:
- id: Label_ops
user_email: testuser@example.com
name: Ops/Review
color_background: "#DDEEFF"
color_text: "#111111"
messages:
- id: msg_welcome
user_email: testuser@example.com
from: welcome@example.com
to: testuser@example.com
subject: Welcome to the Gmail emulator
body_text: You can now test Gmail, Calendar, and Drive flows locally.
label_ids: [INBOX, UNREAD, CATEGORY_UPDATES]
calendars:
- id: primary
user_email: testuser@example.com
summary: testuser@example.com
primary: true
selected: true
time_zone: UTC
calendar_events:
- id: evt_kickoff
user_email: testuser@example.com
calendar_id: primary
summary: Project Kickoff
start_date_time: 2025-01-10T09:00:00.000Z
end_date_time: 2025-01-10T09:30:00.000Z
drive_items:
- id: drv_docs
user_email: testuser@example.com
name: Docs
mime_type: application/vnd.google-apps.folder
parent_ids: [root]
slack:
team:
name: My Workspace
domain: my-workspace
users:
- name: developer
real_name: Developer
email: dev@example.com
channels:
- name: general
topic: General discussion
- name: random
topic: Random stuff
bots:
- name: my-bot
oauth_apps:
- client_id: "12345.67890"
client_secret: example_client_secret
name: My Slack App
redirect_uris:
- http://localhost:3000/api/auth/callback/slack
apple:
users:
- email: testuser@icloud.com
name: Test User
oauth_clients:
- client_id: com.example.app
team_id: TEAM001
name: My Apple App
redirect_uris:
- http://localhost:3000/api/auth/callback/apple
microsoft:
users:
- email: testuser@outlook.com
name: Test User
oauth_clients:
- client_id: example-client-id
client_secret: example-client-secret
name: My Microsoft App
redirect_uris:
- http://localhost:3000/api/auth/callback/microsoft-entra-id
aws:
region: us-east-1
s3:
buckets:
- name: my-app-bucket
- name: my-app-uploads
sqs:
queues:
- name: my-app-events
- name: my-app-dlq
iam:
users:
- user_name: developer
create_access_key: true
roles:
- role_name: lambda-execution-role
description: Role for Lambda function executionThe emulator supports configurable OAuth apps and integrations with strict client validation.
vercel:
integrations:
- client_id: "oac_abc123"
client_secret: "secret_abc123"
name: "My Vercel App"
redirect_uris:
- "http://localhost:3000/api/auth/callback/vercel"github:
oauth_apps:
- client_id: "Iv1.abc123"
client_secret: "secret_abc123"
name: "My Web App"
redirect_uris:
- "http://localhost:3000/api/auth/callback/github"If no oauth_apps are configured, the emulator accepts any client_id (backward-compatible). With apps configured, strict validation is enforced.
Full GitHub App support with JWT authentication and installation access tokens:
github:
apps:
- app_id: 12345
slug:
24D7
"my-github-app"
name: "My GitHub App"
private_key: |
-----BEGIN RSA PRIVATE KEY-----
...your PEM key...
-----END RSA PRIVATE KEY-----
permissions:
contents: read
issues: write
events: [push, pull_request]
webhook_url: "http://localhost:3000/webhooks/github"
webhook_secret: "my-secret"
installations:
- installation_id: 100
account: my-org
repository_selection: allJWT authentication: sign a JWT with { iss: "<app_id>" } using the app's private key (RS256). The emulator verifies the signature and resolves the app.
App webhook delivery: When events occur on repos where a GitHub App is installed, the emulator mirrors real GitHub behavior:
- All webhook payloads (including repo and org hooks) include an
installationfield with{ id, node_id }. - If the app has a
webhook_url, the emulator delivers the event there with theinstallationfield and (if configured) anX-Hub-Signature-256header signed withwebhook_secret.
slack:
oauth_apps:
- client_id: "12345.67890"
client_secret: "example_client_secret"
name: "My Slack App"
redirect_uris:
- "http://localhost:3000/api/auth/callback/slack"apple:
oauth_clients:
- client_id: "com.example.app"
team_id: "TEAM001"
name: "My Apple App"
redirect_uris:
- "http://localhost:3000/api/auth/callback/apple"microsoft:
oauth_clients:
- client_id: "example-client-id"
client_secret: "example-client-secret"
name: "My Microsoft App"
redirect_uris:
- "http://localhost:3000/api/auth/callback/microsoft-entra-id"Every endpoint below is fully stateful with Vercel-style JSON responses and cursor-based pagination.
GET /v2/user- authenticated userPATCH /v2/user- update userGET /v2/teams- list teams (cursor paginated)GET /v2/teams/:teamId- get team (by ID or slug)POST /v2/teams- create teamPATCH /v2/teams/:teamId- update teamGET /v2/teams/:teamId/members- list membersPOST /v2/teams/:teamId/members- add member
POST /v11/projects- create project (with optional env vars and git integration)GET /v10/projects- list projects (search, cursor pagination)GET /v9/projects/:idOrName- get project (includes env vars)PATCH /v9/projects/:idOrName- update projectDELETE /v9/projects/:idOrName- delete project (cascades)GET /v1/projects/:projectId/promote/aliases- promote aliases statusPATCH /v1/projects/:idOrName/protection-bypass- manage bypass secrets
POST /v13/deployments- create deployment (auto-transitions to READY)GET /v13/deployments/:idOrUrl- get deployment (by ID or URL)GET /v6/deployments- list deployments (filter by project, target, state)DELETE /v13/deployments/:id- delete deployment (cascades)PATCH /v12/deployments/:id/cancel- cancel building deploymentGET /v2/deployments/:id/aliases- list deployment aliasesGET /v3/deployments/:idOrUrl/events- get build events/logsGET /v6/deployments/:id/files- list deployment filesPOST /v2/files- upload file (by SHA digest)
POST /v10/projects/:idOrName/domains- add domain (with verification challenge)GET /v9/projects/:idOrName/domains- list domainsGET /v9/projects/:idOrName/domains/:domain- get domainPATCH /v9/projects/:idOrName/domains/:domain- update domainDELETE /v9/projects/:idOrName/domains/:domain- remove domainPOST /v9/projects/:idOrName/domains/:domain/verify- verify domain
GET /v10/projects/:idOrName/env- list env vars (with decrypt option)POST /v10/projects/:idOrName/env- create env vars (single, batch, upsert)GET /v10/projects/:idOrName/env/:id- get env varPATCH /v9/projects/:idOrName/env/:id- update env varDELETE /v9/projects/:idOrName/env/:id- delete env var
Every endpoint below is fully stateful. Creates, updates, and deletes persist in memory and affect related entities.
GET /user- authenticated userPATCH /user- update profileGET /users/:username- get userGET /users- list usersGET /users/:username/repos- list user reposGET /users/:username/orgs- list user orgsGET /users/:username/followers- list followersGET /users/:username/following- list following
GET /repos/:owner/:repo- get repoPOST /user/repos- create user repoPOST /orgs/:org/repos- create org repoPATCH /repos/:owner/:repo- update repoDELETE /repos/:owner/:repo- delete repo (cascades)GET/PUT /repos/:owner/:repo/topics- get/replace topicsGET /repos/:owner/:repo/languages- languagesGET /repos/:owner/:repo/contributors- contributorsGET /repos/:owner/:repo/forks- list forksPOST /repos/:owner/:repo/forks- create forkGET/PUT/DELETE /repos/:owner/:repo/collaborators/:username- collaboratorsGET /repos/:owner/:repo/collaborators/:username/permissionPOST /repos/:owner/:repo/transfer- transfer repoGET /repos/:owner/:repo/tags- list tags
GET /repos/:owner/:repo/issues- list (filter by state, labels, assignee, milestone, creator, since)POST /repos/:owner/:repo/issues- createGET /repos/:owner/:repo/issues/:number- getPATCH /repos/:owner/:repo/issues/:number- update (state transitions, events)PUT/DELETE /repos/:owner/:repo/issues/:number/lock- lock/unlockGET /repos/:owner/:repo/issues/:number/timeline- timeline eventsGET /repos/:owner/:repo/issues/:number/events- eventsPOST/DELETE /repos/:owner/:repo/issues/:number/assignees- manage assignees
GET /repos/:owner/:repo/pulls- list (filter by state, head, base)POST /repos/:owner/:repo/pulls- createGET /repos/:owner/:repo/pulls/:number- getPATCH /repos/:owner/:repo/pulls/:number- updatePUT /repos/:owner/:repo/pulls/:number/merge- merge (with branch protection enforcement)GET /repos/:owner/:repo/pulls/:number/commits- list commitsGET /repos/:owner/:repo/pulls/:number/files- list filesPOST/DELETE /repos/:owner/:repo/pulls/:number/requested_reviewers- manage reviewersPUT /repos/:owner/:repo/pulls/:number/update-branch- update branch
- Issue comments: full CRUD on
/repos/:owner/:repo/issues/:number/comments - Review comments: full CRUD on
/repos/:owner/:repo/pulls/:number/comments - Commit comments: full CRUD on
/repos/:owner/:repo/commits/:sha/comments - Repo-wide listings for each type
GET /repos/:owner/:repo/pulls/:number/reviews- listPOST /repos/:owner/:repo/pulls/:number/reviews- create (with inline comments)GET/PUT /repos/:owner/:repo/pulls/:number/reviews/:id- get/updatePOST /repos/:owner/:repo/pulls/:number/reviews/:id/events- submitPUT /repos/:owner/:repo/pulls/:number/reviews/:id/dismissals- dismiss
- Labels: full CRUD, add/remove from issues, replace all
- Milestones: full CRUD, state transitions, issue counts
- Branches: list, get, protection CRUD (status checks, PR reviews, enforce admins)
- Refs: get, match, create, update, delete
- Commits: get, create
- Trees: get (with recursive), create (with inline content)
- Blobs: get, create
- Tags: get, create
- Orgs: get, update, list
- Org members: list, check, remove, get/set membership
- Teams: full CRUD, members, repos
- Releases: full CRUD, latest, by tag
- Release assets: full CRUD, upload
- Generate release notes
- Repo webhooks: full CRUD, ping, test, deliveries
- Org webhooks: full CRUD, ping
- Real HTTP delivery to registered URLs on all state changes
GET /search/repositories- full query syntax (user, org, language, topic, stars, forks, etc.)GET /search/issues- issues + PRs (repo, is, author, label, milestone, state, etc.)GET /search/users- users + orgsGET /search/code- blob content searchGET /search/commits- commit message searchGET /search/topics- topic searchGET /search/labels- label search
- Workflows: list, get, enable/disable, dispatch
- Workflow runs: list, get, cancel, rerun, delete, logs
- Jobs: list, get, logs
- Artifacts: list, get, delete
- Secrets: repo + org CRUD
- Check runs: create, update, get, annotations, rerequest, list by ref/suite
- Check suites: create, get, preferences, rerequest, list by ref
- Automatic suite status rollup from check run results
GET /rate_limit- rate limit statusGET /meta- server metadataGET /octocat- ASCII artGET /emojis- emoji URLsGET /zen- random zen phraseGET /versions- API versions
OAuth 2.0, OpenID Connect, and mutable Google Workspace-style surfaces for local inbox, calendar, and drive flows.
GET /o/oauth2/v2/auth- authorization endpointPOST /oauth2/token- token exchangeGET /oauth2/v2/userinfo- get user infoGET /.well-known/openid-configuration- OIDC discovery documentGET /oauth2/v3/certs- JSON Web Key Set (JWKS)GET /gmail/v1/users/:userId/messages- list messages withq,labelIds,maxResults, andpageTokenGET /gmail/v1/users/:userId/messages/:id- fetch a Gmail-style message payload infull,metadata,minimal, orrawformatsGET /gmail/v1/users/:userId/messages/:messageId/attachments/:id- fetch attachment bodiesPOST /gmail/v1/users/:userId/messages/send- create sent mail fromrawMIME or structured fieldsPOST /gmail/v1/users/:userId/messages/import- import inbox mailPOST /gmail/v1/users/:userId/messages- insert a message directlyPOST /gmail/v1/users/:userId/messages/:id/modify- add/remove labels on one messagePOST /gmail/v1/users/:userId/messages/batchModify- add/remove labels across many messagesPOST /gmail/v1/users/:userId/messages/:id/trashandPOST /gmail/v1/users/:userId/messages/:id/untrashGET /gmail/v1/users/:userId/drafts,POST /gmail/v1/users/:userId/drafts,GET /gmail/v1/users/:userId/drafts/:id,PUT /gmail/v1/users/:userId/drafts/:id,POST /gmail/v1/users/:userId/drafts/:id/send,DELETE /gmail/v1/users/:userId/drafts/:idPOST /gmail/v1/users/:userId/threads/:id/modify- add/remove labels across a threadGET /gmail/v1/users/:userId/threadsandGET /gmail/v1/users/:userId/threads/:idGET /gmail/v1/users/:userId/labels,POST /gmail/v1/users/:userId/labels,PATCH /gmail/v1/users/:userId/labels/:id,DELETE /gmail/v1/users/:userId/labels/:idGET /gmail/v1/users/:userId/history,POST /gmail/v1/users/:userId/watch,POST /gmail/v1/users/:userId/stopGET /gmail/v1/users/:userId/settings/filters,POST /gmail/v1/users/:userId/settings/filters,DELETE /gmail/v1/users/:userId/settings/filters/:idGET /gmail/v1/users/:userId/settings/forwardingAddresses,GET /gmail/v1/users/:userId/settings/sendAsGET /calendar/v3/users/:userId/calendarList,GET /calendar/v3/calendars/:calendarId/events,POST /calendar/v3/calendars/:calendarId/events,DELETE /calendar/v3/calendars/:calendarId/events/:eventId,POST /calendar/v3/freeBusyGET /drive/v3/files,GET /drive/v3/files/:fileId,POST /drive/v3/files,PATCH /drive/v3/files/:fileId,PUT /drive/v3/files/:fileId,POST /upload/drive/v3/files
Fully stateful Slack Web API emulation with channels, messages, threads, reactions, OAuth v2, and incoming webhooks.
POST /api/auth.test- test authenticationPOST /api/chat.postMessage- post message (supports threads viathread_ts)POST /api/chat.update- update messagePOST /api/chat.delete- delete messagePOST /api/chat.meMessage- /me message
POST /api/conversations.list- list channels (cursor pagination)POST /api/conversations.info- get channel infoPOST /api/conversations.create- create channelPOST /api/conversations.history- channel historyPOST /api/conversations.replies- thread repliesPOST /api/conversations.join/conversations.leave- join/leavePOST /api/conversations.members- list members
POST /api/users.list- list users (cursor pagination)POST /api/users.info- get user infoPOST /api/users.lookupByEmail- lookup by emailPOST /api/reactions.add/reactions.remove/reactions.get- manage reactions
POST /api/team.info- workspace infoPOST /api/bots.info- bot infoPOST /services/:teamId/:botId/:webhookId- incoming webhook
GET /oauth/v2/authorize- authorization (shows user picker)POST /api/oauth.v2.access- token exchange
Sign in with Apple emulation with authorization code flow, PKCE support, RS256 ID tokens, and OIDC discovery.
GET /.well-known/openid-configuration- OIDC discovery documentGET /auth/keys- JSON Web Key Set (JWKS)GET /auth/authorize- authorization endpoint (shows user picker)POST /auth/token- token exchange (authorization code and refresh token grants)POST /auth/revoke- token revocation
Microsoft Entra ID (Azure AD) v2.0 OAuth 2.0 and OpenID Connect emulation with authorization code flow, PKCE, client credentials, RS256 ID tokens, and OIDC discovery.
GET /.well-known/openid-configuration- OIDC discovery documentGET /:tenant/v2.0/.well-known/openid-configuration- tenant-scoped OIDC discoveryGET /discovery/v2.0/keys- JSON Web Key Set (JWKS)GET /oauth2/v2.0/authorize- authorization endpoint (shows user picker)POST /oauth2/v2.0/token- token exchange (authorization code, refresh token, client credentials)GET /oidc/userinfo- OpenID Connect user infoGET /v1.0/me- Microsoft Graph user profileGET /oauth2/v2.0/logout- end session / logoutPOST /oauth2/v2.0/revoke- token revocation
S3, SQS, IAM, and STS emulation with REST-style S3 paths and query-style SQS/IAM/STS endpoints. All responses use AWS-compatible XML.
GET /s3/- list all bucketsPUT /s3/:bucket- create bucketDELETE /s3/:bucket- delete bucketHEAD /s3/:bucket- check existenceGET /s3/:bucket- list objects (prefix, delimiter, max-keys)PUT /s3/:bucket/:key- put object (supports copy viax-amz-copy-source)GET /s3/:bucket/:key- get objectHEAD /s3/:bucket/:key- head objectDELETE /s3/:bucket/:key- delete object
All operations via POST /sqs/ with Action parameter:
CreateQueue,ListQueues,GetQueueUrl,GetQueueAttributesSendMessage,ReceiveMessage,DeleteMessagePurgeQueue,DeleteQueue
All operations via POST /iam/ with Action parameter:
CreateUser,GetUser,ListUsers,DeleteUserCreateAccessKey,ListAccessKeys,DeleteAccessKeyCreateRole,GetRole,ListRoles,DeleteRole
All operations via POST /sts/ with Action parameter:
GetCallerIdentity,AssumeRole
Embed emulators directly in your Next.js app so they run on the same origin. This solves the Vercel preview deployment problem where OAuth callback URLs change with every deployment.
npm install @emulators/adapter-next @emulators/github @emulators/googleOnly install the emulators you need. Each @emulators/* package is published independently.
Create a catch-all route that serves emulator traffic:
// app/emulate/[...path]/route.ts
import { createEmulateHandler } from '@emulators/adapter-next'
import * as github from '@emulators/github'
import * as google from '@emulators/google'
export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({
services: {
github: {
emulator: github,
seed: {
users: [{ login: 'octocat', name: 'The Octocat' }],
repos: [{ owner: 'octocat', name: 'hello-world', auto_init: true }],
},
},
google: {
emulator: google,
seed: {
users: [{ email: 'test@example.com', name: 'Test User' }],
},
},
},
})Point your provider at the emulator paths on the same origin:
import GitHub from 'next-auth/providers/github'
const baseUrl = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: 'http://localhost:3000'
GitHub({
clientId: 'any-value',
clientSecret: 'any-value',
authorization: { url: `${baseUrl}/emulate/github/login/oauth/authorize` },
token: { url: `${baseUrl}/emulate/github/login/oauth/access_token` },
24D7
userinfo: { url: `${baseUrl}/emulate/github/user` },
})No oauth_apps need to be seeded. When none are configured, the emulator skips client_id, client_secret, and redirect_uri validation.
Emulator UI pages use bundled fonts. Wrap your Next.js config to include them in the serverless trace:
// next.config.mjs
import { withEmulate } from '@emulators/adapter-next'
export default withEmulate({
// your normal Next.js config
})If you mount the catch-all at a custom path, pass the matching prefix:
export default withEmulate(nextConfig, { routePrefix: '/api/emulate' })By default, emulator state is in-memory and resets on every cold start. To persist state across restarts, pass a persistence adapter:
import { createEmulateHandler } from '@emulators/adapter-next'
import * as github from '@emulators/github'
const kvAdapter = {
async load() { return await kv.get('emulate-state') },
async save(data: string) { await kv.set('emulate-state', data) },
}
export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({
services: { github: { emulator: github } },
persistence: kvAdapter,
})For local development, @emulators/core ships filePersistence:
import { filePersistence } from '@emulators/core'
// ...
persistence: filePersistence('.emulate/state.json'),The persistence adapter is called on cold start (load) and after every mutating request (save). Saves are serialized via an internal queue to prevent race conditions.
packages/
emulate/ # CLI entry point (commander)
@emulators/
core/ # HTTP server, in-memory store, plugin interface, middleware
adapter-next/ # Next.js App Router integration
vercel/ # Vercel API service
github/ # GitHub API service
google/ # Google OAuth 2.0 / OIDC + Gmail, Calendar, Drive
slack/ # Slack Web API, OAuth v2, incoming webhooks
apple/ # Apple Sign In / OIDC
microsoft/ # Microsoft Entra ID OAuth 2.0 / OIDC + Graph /me
aws/ # AWS S3, SQS, IAM, STS
apps/
web/ # Documentation site (Next.js)
The core provides a generic Store with typed Collection<T> instances supporting CRUD, indexing, filtering, and pagination. Each service plugin registers its routes on the shared Hono app and uses the store for state.
Tokens are configured in the seed config and map to users. Pass them as Authorization: Bearer <token> or Authorization: token <token>.
Vercel: All endpoints accept teamId or slug query params for team scoping. Pagination uses cursor-based limit/since/until with pagination response objects.
GitHub: Public repo endpoints work without auth. Private repos and write operations require a valid token. Pagination uses page/per_page with Link headers.
Google: Standard OAuth 2.0 authorization code flow. Configure clients in the seed config.
Slack: All Web API endpoints require Authorization: Bearer <token>. OAuth v2 flow with user picker UI.
Apple: OIDC authorization code flow with RS256 ID tokens. On first auth per user/client pair, a user JSON blob is included.
Microsoft: OIDC authorization code flow with PKCE support. Also supports client credentials grants. Microsoft Graph /v1.0/me available.
AWS: Bearer tokens or IAM access key credentials. Default key pair always seeded: AKIAIOSFODNN7EXAMPLE / wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY.