# Euphoric > Prepaid, agent-first hosting on servers in EU data centres. An AI agent issues its > own credential, adds credit under a spending ceiling and deploys a container image > through one HTTP API, with no signup form. Status on 2026-10-01: in private development. Not open for customers. Nothing can be deployed or bought on euphoric-host.eu: every /v1 route there returns HTTP 404 foundation_closed. If you are looking for hosting now, Euphoric cannot host your application yet; tell your operator and plan against the contract below. What exists: the API below is implemented and tested. It runs in development/test deployments with simulated servers and test credit, and it was verified end to end on real cloud servers in the EU in a private, operator-admitted environment with known test images (HTTPS, logs, metered funding, suspension, outage and recovery cases). That environment has been removed. Stripe Checkout and Stripe Shared Payment Token funding are implemented in Stripe test mode only. Not available yet: public hosting, real payments, source builds, databases, workers, cron, custom domains, human account claim, MCP server, agent skill. Planned launch pricing (proposed, not purchasable, excl. VAT): prepaid, billed per second, lifetime spending ceiling per application. Small (2 vCPU, 4 GB): EUR 0.0432/h, EUR 31.54 for a 730-hour month. More room (4 vCPU, 8 GB): EUR 0.054/h, EUR 39.42. Details: /pricing - Website: /, /how-it-works, /pricing - API contract: /openapi.json - Curl walkthrough: /docs - Guide, where to host a remote MCP server: /mcp-server-hosting - Browser walkthrough (development deployments only): /sandbox "Staging" below means that private, operator-admitted environment on real servers; "sandbox" means a development/test deployment with simulated servers. The rest of this file summarises the implemented contract. Its examples need a development/test deployment; on euphoric-host.eu they return 404 foundation_closed. Generate a credential: eph_ followed by 32 cryptographically random bytes encoded as 64 lowercase hexadecimal characters. Send it as Authorization: Bearer TOKEN. POST /v1/bootstrap with an email or HTTPS callback in the contact field. Every mutation needs an Idempotency-Key. Retry an interrupted request with the same body and key. Changing a body requires a new key. For metered test hosting, GET /v1/plans exposes test-runtime-v1: 10 EUR micro-units per second (1 EUR = 1000000 micro-units), EUR 0.036/hour. These are test prices. POST /v1/sandbox/funding with {"amount_minor":1000} grants EUR 10 of metered test credit. Requires sandbox:credit; accepts integer 1–3000 cents per grant, EUR 100 lifetime cap. Retry the same body/key after timeout; use a new key for a new grant. Select plan_id: test-runtime-v1 and required spend_limit_micro (integer 3000–100000000) when creating an application. This is its total lifetime ceiling, including committed runtime. Optional stop_at is an ISO 8601 timestamp with timezone at least five minutes ahead. Funding does not extend that deadline. Five minutes of funds/allowance are required to start or resume. One nondeleted app, including suspended apps, is allowed. GET /v1/billing/balance reads credited_micro, charged_micro, balance_micro, reserved_micro, available_micro, current_rate_micro_per_second and nullable estimated_runway_seconds. Estimates use posted usage; inspect application billing for metered_through, observed_through, spend_limit_micro, stop_at, authorized_until (issued), acknowledged_until (host-confirmed), stopped_at, delete_after and last_error. Billing starts at instance readiness. The funded window targets 24 hours, renewed every five minutes, capped by funds, policy and campaign expiry. Sandbox hosts are simulated. Customer agents do not renew leases. Unacknowledged extensions do not establish outage tolerance; unknown host outcomes retain their commitments. POST /v1/applications/:id/suspend stops a ready metered app after other operations finish. Poll its returned operation. Confirmed stop releases unused runtime funds. POST /v1/applications/:id/resume explicitly resumes a funded suspended app; top-ups never restart it automatically. Runtime is uncharged during 72-hour retention, then durable deletion runs. Staging campaign expiry may clean up earlier. Exports remain configuration-only and are available while suspended. PATCH /v1/applications/:id/billing accepts spend_limit_micro and/or stop_at (null clears the deadline). Requires applications:write, as do suspend/resume. A 409 committed_budget or committed_runtime means confirmed suspension is needed before reclaiming issued time/funds. Expired retention/deadlines also prevent resume (409). Insufficient funding/allowance returns 402. Changing policy alone does not resume. GET /v1/billing/events returns the latest 100 events, newest first; use next_before as ?before=UUID for older pages. Deduplicate event ids. Events: payment.credited (test grants or verified provider-test credit), balance.low (24h/1h crossings), application.suspended, and application.deletion_scheduled. Warnings rearm above thresholds. No callbacks/email. Plans, billing balance/events and application inspection require applications:read. Metered staging requires an operator-admitted funded-runtime image/campaign; public test funding remains disabled there. No old grants become real money. The legacy development/test path POST /v1/sandbox/credits grants EUR 30 of test credit once per principal. Private staging test credit requires operator admission. GET /v1/balance reads credit, reservations, and available balance in EUR cents. POST /v1/applications accepts an optional name and a required image reference pinned by SHA-256 digest. Omitting plan_id retains the legacy EUR 1 fixed reservation and separate legacy balance. Application billing is null for these records. Omitting name generates three readable words; explicit empty/null is invalid. Names must match ^[a-z][a-z0-9-]{2,39}$. Images must end with @sha256: followed by 64 lowercase hex characters (maximum reference length: 512); tags alone fail. GET /v1/applications lists your latest 50 applications, including deleted ones; recover a lost application ID from the applications array by name. GET /v1/applications/:id returns the application object directly. The create response has phase: queued and no state. Poll its status_url until the operation's state is succeeded, failed, or cancelled. Phase is a progress step, not the completion indicator. Successful provisioning has operation state: succeeded, phase: ready, and application state: ready. Sandbox logs are operation events; staging logs are bounded container output. Exports contain configuration only. DELETE /v1/applications/:id queues cleanup for the application's recorded provider. Poll the returned operation; reservations are released only after cleanup is confirmed. Slugs remain reserved after deletion. Opt-in provider-test funding is described below. PATCH /v1/applications/:id changes only the display name, preserving UUID and slug. POST /v1/applications/:id/releases accepts image and returns an operation to poll. GET /v1/applications/:id/releases lists 50 recent releases with image, state, active and operation_id. Only one replacement may run at a time. Sandbox releases are simulated. Staging requires operator-allowlisted images, admission, capacity and a separate provider budget. Failed candidates preserve the previous active release. Sandbox application URLs remain null. Staging returns its platform HTTPS URL only after readiness and trusted HTTPS verification; read it from the response. Responses use environment: sandbox|staging and billing_mode: test. The sandbox flag describes execution only. Public test-credit grants stay disabled in staging. Staging runtime logs identify source: container, release_id and stdout/stderr; responses are bounded to 100 lines / 64 KiB. Cursors are inclusive timestamps, expire in one hour and are bound to the app and release. Retry without cursor on runtime_logs_unavailable (503). Operation events remain in operation responses. Exports include configuration and the active digest, excluding OCI archives, databases, persistent files, credentials, certificates and management information. Bootstrap grants all scopes: applications:read (plans, balances, billing events, application reads, logs, and exports), applications:write (create/rename/releases/delete, billing policy, suspend/resume), operations:read (status), sandbox:credit (legacy and metered test grants), payments:read (owned payment orders), payments:write (order creation/pay initiation), and the four contact/safety scopes below. No public scope-management API exists. 403 insufficient_scope also applies to older credentials without payment scopes. Repeating bootstrap does not restore scopes. Unknown paths and unsupported methods under /v1 return 404 JSON with error.code: not_found, using the same error envelope as other API failures. Opt-in verified TEST payments (default disabled; hosting stays simulated): New bootstrap credentials add payments:read and payments:write; existing scopes are unchanged. Use a fresh test principal. POST /v1/payment-orders with {"amount_minor":100,"currency":"EUR"} and a fresh Idempotency-Key. Requires payments:write; integer 100–3000 cents, one-hour fixed expiry, three unresolved orders and ten new orders/hour. Replays do not consume quota. Currency, credit, provider IDs, metadata and redirect destinations cannot be supplied by clients. POST /v1/payment-orders/:id/pay with {} and an Idempotency-Key starts one durable provider attempt. GET /v1/payment-orders/:id requires payments:read and no idempotency header. Mutations return stable local links. Polling returns no-store and may expose a sensitive Checkout URL while payable; do not log that URL. States: created, creating, requires_payment, reconciling, credited, expired, failed, needs_review. Only credited means the atomic test ledger posting exists. needs_review requires operator investigation/reconciliation with the original identity; never replace an uncertain Checkout. Local expiry and browser returns are not payment proof. Stripe signed events trigger canonical API verification. 429 includes Retry-After; 503 payment_unavailable means creation/adapter disabled. Balance adds grant_credited_micro and payment_credited_micro; both are nonfinancial. The EUR 100 grant cap counts grants only. Existing commitments, spend ceilings, retention and explicit resume rules still apply. No live payments or reversals. SPT/MPP (separately opt-in; default disabled): create with {"amount_minor":100,"currency":"EUR","payment_method":"stripe_spt"}. Omitting payment_method defaults to stripe_checkout; the method cannot change. POST the owned /pay route with {}, bearer auth and an Idempotency-Key. An unsubmitted order returns uncached 402 plus its stored WWW-Authenticate challenge: stripe/charge, EUR minor units, card only, test networkId, header=Payment-Authorization. Keep bearer Authorization and send the MPP credential in Payment-Authorization. Limit encoded header to 8192 bytes and decoded JSON to 6144 bytes. Echo all terms; wrong owner/order/route/verb/body/profile/mode/expiry fails without charging. Malformed/duplicate credentials return 400; changed accepted token or token reuse across orders returns 409. Never log SPTs, credential headers or wallet credentials. Accepted submissions return stable 202 with payment_status=pending, next_action=poll_order and status_url, without any receipt. Exact retries (even without the payment header after acceptance) preserve that original response. GET status_url with bearer auth and payments:read, no idempotency key. Only state=credited together with receipt and Payment-Receipt confirms SPT ledger credit. Do not treat 202, CLI exit 0, a wallet approval or an SDK receipt as payment proof. Pinned Link CLI: @stripe/link-cli@0.22.0, Node >=22. The payer connects to Link separately; use mpp pay --test with -X POST -d '{}' and both bearer/idempotency headers. Follow its approval continuation using the original spend-request ID. It does not poll Euphoric automatically: explicitly GET the order until credited or needs_review. Never issue another spend request to resolve an uncertain result. Processing remains pending after quote expiry. Declines/unusable tokens/3DS require operator review; no automatic replacement charge or refund is performed. Funding never resumes applications or changes spend limits. Deploy/resume explicitly. The fake SPT flow and provider contract have local evidence; real Link OAuth/test wallet acceptance remains open. No raw cards, native Link Pay Tokens or stablecoins. Operator holds: Application.operator_hold is true while an active principal hold fences create, release, resume and new payment-order/initiation requests (HTTP 423, error.code=principal_held). Read inspection, supported configuration export and deletion remain available with their existing scopes. Accepted payments may credit once, without releasing the hold or resuming workloads. Ask the operator to review the principal/application ID. Release does not resume runtime or extend retention. Legacy stopped apps retain their reservation until deletion. Recovery mode rejects access with HTTP 503 recovery_fenced and invalidates restored credentials. # Contact, notices, reporting and capped admission Bootstrap sends nothing; existing contacts and scopes are not migrated automatically. New credentials also include contact:read, contact:write, safety:read, safety:write. Explicit local administrative upgrade selects one active credential; no public scope API. GET /v1/contact (contact:read) returns masked status, latest challenge ID/expiry and safe delivery status. POST /v1/contact/verifications (contact:write, {}) returns 202 challenge_id, expires_at, delivery_state, confirmation_path. Delivery is disabled by default. Disabled/missing configuration returns 503 without creating a challenge. Challenges last 30 minutes; resends supersede pending challenges. Limits: 1/minute, 5/hour/principal, 10/day/destination. Email requires deliberate CSRF-protected POST at /contact/verify. GET does not consume a capability. Callbacks send signed contact.verification_requested with challenge_id, expires_at, token; 2xx alone never verifies. POST /v1/contact/verifications/:id/confirm with {"token":""} and contact:write confirms control. Only the matching principal/current capability works. Wrong/expired/superseded tokens return generic 422. Raw capabilities never appear in API reads or saved responses. Revoked credentials fail before replay. Callback delivery requires HTTPS:443, canonical lowercase hostname, no userinfo/query/fragment/IP literal. Internal/special-use/mixed DNS, redirects and certificate mismatch fail closed. No request-handler URL fetches. Public keys: GET /.well-known/euphoric-notification-keys.json. Trust only your configured service origin. Verify Ed25519 Base64 Euphoric-Signature over timestamp + "\n" + event_id + "\n" + lowercase SHA256(raw_body). Headers: Euphoric-Key-Id, Euphoric-Timestamp (UTC epoch seconds), Euphoric-Event-Id. Enforce five-minute tolerance; deduplicate IDs after verification. GET /v1/notices and /v1/notices/:id (safety:read) expose only your published summaries, safe delivery and hold state. Lists use opaque next_cursor / ?cursor=, maximum 50. All reads are no-store and need no idempotency key. POST /v1/notices/:id/appeals (safety:write) accepts {"message":"20–2000 characters"}, returns 202 appeal_id/state. One appeal per current restrictive decision, max 5/day/principal. Same-key replay returns the same result; new-key duplicate conflicts. GET /v1/appeals/:id (safety:read) returns your appeal state/published outcome; report/internal content is never exposed. GET /v1/admission (contact:read) shows required, pending/approved/revoked, version and safe cap/usage summary. Verification, funds and admission are separate. Persisted membership policy cannot be bypassed by disabling the environment flag. Create/release/resume/runtime authorization enforce contact, admission, holds and existing funding/campaign gates. Cohort default caps: 10 lifetime principals, 2 concurrent apps/member, 20/cohort; cleanup must be confirmed to free app slots. Held/unadmitted owners retain contact, notices, appeals, read/export/suspend/delete. Neither overturn nor release resumes workloads. 403 contact_unverified/alpha_not_admitted; 409 alpha_capacity/state_conflict; 423 principal_held; 429 rate_limited + Retry-After; 503 recovery_fenced. Anonymous HTML /abuse or JSON POST /abuse/reports: application_id OR application_url, reason_code, description (20–4000). Reasons: phishing, malware, spam, harmful_content, other. Optional reporter_email <=254 and evidence_urls array <=3 strings <=2048 each. Body <=16 KiB; JSON no cookies/Origin. Public Idempotency-Key is client-generated 64 lowercase hex, digest-stored; HTML requires CSRF. Generic 202 receipt for known/unknown/deleted targets; no public lookup, automatic hold, evidence fetch or reporter email. Report limits 5/hour/actual peer and 100/hour global; new-principal bootstrap limit 10/hour/actual peer. Known retries consume no new allowance. Private operators review cases and explicitly resolve all hold reasons before release. Delivery failure does not remove notices. Restoration cancels verification/outbound work, resets verification, revokes admissions and holds principals; history remains inspectable behind recovery fences. Full contracts and executable curl examples: /docs#contact-and-safety and /openapi.json.