This guide mirrors the checked-in examples/accrue_host story in package-facing terms. Your Phoenix app owns MyApp.Billing, routing, auth, runtime config, and verification choices. Accrue owns the billing engine behind those public boundaries. Read-only processor queries such as saved payment methods use Accrue.Billing.list_payment_methods/2 (and the host wrapper MyApp.Billing.list_payment_methods/2 after mix accrue.install); see guides/telemetry.md for the [:accrue, :billing, :payment_method, :list] span. Server-side Stripe Checkout session creation uses Accrue.Billing.create_checkout_session/2 (and your host facade after install); telemetry is [:accrue, :billing, :checkout_session, :create] — see guides/telemetry.md#billing-checkout-session-create.

Customer Portal session creation is the parallel server-side helper for Stripe-hosted billing self-service.

Server-side Stripe Customer Portal session creation uses Accrue.Billing.create_billing_portal_session/2 (and your host facade after install); telemetry is [:accrue, :billing, :billing_portal, :create] — see guides/telemetry.md#billing-billing-portal-create.

How to enter this guide

This guide is one spine with three entry capsules — pick where you are starting, then follow the same ordered story (deps → install → runtime → migrations → Oban → webhooks → admin → proof). Public wording and step order stay aligned with examples/accrue_host/README.md; when the spine or command vocabulary changes, update that README in the same pull request (D-02). Maintainer checklist (INT-11): same-PR capsule discipline lives in the contributor map scripts/ci/README.md — search for First Hour + host README capsule parity.

For maintenance posture (when to stop speculative doc work, how friction is intake-gated), see Maturity and maintenance.

Capsule H — Hex consumer

You already have a Phoenix app. Add Accrue to mix.exs, run mix deps.get, then mix accrue.install … and continue from § 1. First run below (runtime config → migrations → Oban → webhook route → admin mount → subscription + proof).

Capsule M — Monorepo clone

From the repository root: cd examples/accrue_host, run mix setup, start mix phx.server, then follow the numbered host README story (subscription → signed webhook → admin → mix verify) — the same Fake-backed arc this guide describes in package terms. Sigra is wired in the checked-in demo for convenience and reproducibility, not because production Accrue apps must use it.

Capsule R — Evaluate / read-only

Shortest read-only path: clone the repo, cd examples/accrue_host, run mix verify or mix verify.full. For merge-blocking VERIFY-01 detail and Playwright entry points, use #proof-and-verification in the host README when you need more than the bounded proof commands.

Trust boundary (production vs demo)

Production apps integrate billing through host-owned Accrue.Auth; see Auth adapters for adapter choices and wiring contracts. Sigra is optional: the demo uses it for deterministic organization billing and CI, not as a blanket production requirement. When you are not on Sigra, follow Capsule H and Organization billing (non-Sigra) for org-scoped Stripe customers. Demo-specific mix.exs and setup commands stay in examples/accrue_host/README.md.

When you are preparing a real deploy (not the demo loop), walk Production readiness once — it links the same guides in ship order without duplicating them.

1. First run

The first hour should end with one Fake-backed subscription, one signed webhook proof, mounted admin inspection, and a focused verification pass.

Install the packages

Hex vs main: The version pins below mirror accrue/mix.exs and accrue_admin/mix.exs @version on the branch you are reading (usually main on GitHub). Hex.pm / Hex.pm/packages/accrue_admin reflect what is published; use HexDocs when you need docs tied to the resolved Hex version.

  1. The fenced ~> pins below track the Hex-published SemVer line for the @version pair this branch ships with.
  2. path: / monorepo installs must keep accrue and accrue_admin on the same three-part ~> (lockstep trains).
  3. Pre-1.0 ~> minors may still ship breaking API changes—treat mix.lock as the production stability boundary, not semver intuition alone.
defp deps do
  [
    {:accrue, "~> 1.0.0"},
    {:accrue_admin, "~> 1.0.0"}
  ]
end
mix deps.get
mix accrue.install --billable MyApp.Accounts.User --billing-context MyApp.Billing

The checked-in host example is the canonical local evaluation loop:

cd examples/accrue_host
mix setup
mix phx.server

Keep runtime config host-owned

Accrue raises Accrue.ConfigError when required setup is missing. Keep secrets and environment-specific values in config/runtime.exs:

import Config

config :accrue, :processor, Accrue.Processor.Fake

config :accrue, repo: MyApp.Repo

config :accrue, :webhook_signing_secrets, %{
  stripe: System.get_env("STRIPE_WEBHOOK_SECRET", "whsec_test_host")
}

Run your database setup before boot:

mix ecto.create
mix ecto.migrate

Start Oban with the app so webhook dispatch and replay work end to end:

children = [
  MyApp.Repo,
  {Oban, Application.fetch_env!(:my_app, Oban)},
  MyAppWeb.Endpoint
]

Mount the public billing boundaries

Add signed webhook ingest at /webhooks/stripe and keep the handler on the public callback surface:

defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  import Accrue.Router

  pipeline :accrue_webhook_raw_body do
    plug Plug.Parsers,
      parsers: [:json],
      pass: ["*/*"],
      json_decoder: Jason,
      body_reader: {Accrue.Webhook.CachingBodyReader, :read_body, []}
  end

  scope "/webhooks" do
    pipe_through :accrue_webhook_raw_body
    accrue_webhook "/stripe", :stripe
  end
end

When this fails

defmodule MyApp.BillingHandler do
  use Accrue.Webhook.Handler

  @impl Accrue.Webhook.Handler
  def handle_event(type, event, ctx) do
    MyApp.Billing.handle_webhook(type, event, ctx)
  end
end

Mount accrue_admin "/billing" behind your host auth boundary. AccrueAdmin.Router.accrue_admin/2 is the public router macro:

import AccrueAdmin.Router

scope "/" do
  pipe_through [:browser, :require_authenticated_user]

  accrue_admin "/billing",
    session_keys: [:user_token],
    on_mount: [{MyAppWeb.UserAuth, :mount_current_user}]
end

Prove the first subscription and webhook

Create the first subscription through the generated facade:

user = MyApp.Accounts.get_user!(user_id)

{:ok, subscription} =
  MyApp.Billing.subscribe(user, "price_basic", trial_end: {:days, 14})

For app-level tests, stay on supported helpers:

use Accrue.Test

Then post one signed customer.subscription.created payload through /webhooks/stripe, visit /billing, and confirm the mounted admin UI shows the resulting billing state plus replay visibility.

Finish the guided path with the focused host proofs:

mix verify

mix verify is the focused tutorial proof suite. mix verify.full is the CI-equivalent local gate that adds compile, assets, dev boot, regression, and browser smoke after the first-run story is already clear. For the authoritative merge-blocking command matrix, VERIFY-01, and Playwright entry points, see Proof and verification in the host demo README.

2. Seeded history

Seeded history is for deterministic replay/history evaluation, not for the main teaching path.

cd examples/accrue_host
mix setup
mix verify.full

Use it when you need replay-ready webhook states, browser smoke fixtures, or other evaluation setup that should not become public integration guidance.

3. Focused verification

  • mix verify proves the host-owned tutorial arc: installer boundary, first subscription through MyApp.Billing, signed webhook ingest, mounted /billing inspection, and replay visibility.
  • mix verify.full is the CI-equivalent local gate for maintainers.
  • bash scripts/ci/accrue_host_uat.sh is the repo-root wrapper around that same full contract.
  • bash scripts/ci/accrue_host_hex_smoke.sh is Hex smoke and stays separate from the checked-in host demo.
  • mix accrue.install remains the production setup command for your own host app.

4. Rerunning mix accrue.install

Reruns refresh pristine generated files that still match the Accrue fingerprint marker; user-edited generated files are skipped so local policy changes are preserved. Unmarked existing files stay skipped unless you opt into a narrow overwrite, and --write-conflicts writes reviewable artifacts under .accrue/conflicts/ instead of patching live files blindly — the same contract as the upgrade guide. See Upgrade guide — Installer rerun behavior for the full installer rerun semantics.