Embedding
이 콘텐츠는 아직 번역되지 않았습니다.
A deployed machine answers on your organization’s cloud host at https://{your-org}.mashin.cloud/{machine-slug}. If the machine declares a page surface, that URL is a web page. If it declares an api surface, the same host answers JSON. Embedding is a question of which of those you pull into your own site.
Two paths need nothing installed: an iframe, and the HTTP API. Both are described first because both work today against any deployment.
iframe
Section titled “iframe”The simplest embed. Point an iframe at the machine’s page URL and add ?embed=true:
<iframe src="https://your-org.mashin.cloud/dashboard?embed=true" style="width: 100%; height: 400px; border: none;"></iframe>?embed=true strips the page chrome: no header, no footer, no [*] peek button, no page padding. The machine renders exactly as it would standalone, minus the frame.
The iframe carries the browser’s own session. A page surface defaults to auth: session, so a visitor who is not signed in to your organization sees the sign-in path, not the machine. To embed a page for anonymous visitors, declare auth: public on the page surface. Do not put an API key in the iframe URL: page surfaces do not read one from the query string, and a key in a URL is a key in every referrer header and browser history.
Which sites may frame the page is controlled by the deployment, not by the machine source. See Controlling who can embed.
HTTP API
Section titled “HTTP API”For full control over rendering, call the machine and draw the output yourself. The machine needs an api surface.
const response = await fetch("https://your-org.mashin.cloud/api/revenue-report/run", { method: "POST", headers: { "Authorization": "Bearer msk_your_deployment_key", "Content-Type": "application/json" }, body: JSON.stringify({ input: { period: "monthly" } })});
const { success, output, run_id, duration_ms } = await response.json();// Render output.revenue, output.orders with your own templatesOn success the body is { "success": true, "output": {...}, "run_id": "...", "duration_ms": N }. On failure it is { "success": false, "error": "...", "duration_ms": N } with a 500.
There is also an organization-level endpoint that takes the machine name in the body, for callers that cannot vary the path:
POST https://your-org.mashin.cloud/api/run{ "machine": "revenue-report", "input": { "period": "monthly" } }msk_ keys are deployment API keys. They belong on a server, never in browser code.
Access tokens for browser code
Section titled “Access tokens for browser code”Frontend code should not hold an msk_ key. Exchange it server-side for a short-lived access token and hand that to the browser:
curl -X POST https://your-org.mashin.cloud/api/platform/deployments/{deployment_id}/access-tokens \ -H "Authorization: Bearer msk_your_deployment_key" \ -H "Content-Type: application/json" \ -d '{"expires_in": 3600}'The response is { "access_token": "mat_...", "token_type": "bearer", "expires_in": 3600, "expires_at": "..." }. Tokens default to one hour and are capped at 24 hours. mat_ tokens are the ones that are safe to ship to a browser.
The web component
Section titled “The web component”@mashin/embed defines a <mashin-machine> custom element that renders a machine as a chat, a form, or a read-only display. It is framework-free: any framework that can render an HTML tag can host it, which is why there is no separate Phoenix, Rails, Svelte, or Angular integration to install.
<mashin-machine machine="summarizer" base-url="https://your-org.mashin.cloud" mode="chat" theme="auto" access-token="mat_short_lived_token"></mashin-machine>Those five attributes are the whole surface:
| Attribute | Values | Default | Purpose |
|---|---|---|---|
machine |
machine slug | required | Which machine to run |
base-url |
deployment host | required | Where the machine lives |
mode |
chat, form, display |
chat |
How the machine is presented |
theme |
light, dark, auto |
auto |
Color scheme |
access-token |
mat_... |
none | Short-lived auth token |
The element emits mashin:ready, mashin:run-start, mashin:run-complete, mashin:message, and mashin:error as CustomEvents, so the host page listens with addEventListener and nothing else.
The package lives in the mashin repository under packages/embed. It is not on npm yet, so for now it is built from source rather than installed.
Framework SDKs
Section titled “Framework SDKs”Three packages wrap the same runtime for people who would rather write components than tags. They live in the mashin repository under packages/ and are not published to a package registry yet, so today they are consumed from source.
@mashin/react
Section titled “@mashin/react”MashinProvider configures the client once. Hooks and components read it from context.
import { MashinProvider, useMachine } from "@mashin/react";
function App() { return ( <MashinProvider baseUrl="https://your-org.mashin.cloud" accessToken={token}> <Summary articleBody={body} /> </MashinProvider> );}
function Summary({ articleBody }: { articleBody: string }) { const { run, isLoading, data, error } = useMachine("summarizer");
return ( <div> <button onClick={() => run({ text: articleBody })} disabled={isLoading}> {isLoading ? "Summarizing..." : "Summarize"} </button> {error && <p>{error.message}</p>} {data?.output && <p>{String(data.output.summary)}</p>} </div> );}MashinProvider takes the client config (baseUrl, apiKey, accessToken, timeout) plus children. useMachine(machine) returns { isLoading, data, error, streamContent, streamEvents } alongside run, stream and reset. data is a RunResult: { runId, status, output?, error? }.
For the web component in React, MashinMachine is a typed wrapper over the same custom element:
import { MashinMachine } from "@mashin/react";
<MashinMachine machine="summarizer" baseUrl="https://your-org.mashin.cloud" mode="chat" theme="auto" accessToken={token} onRunComplete={(result) => console.log(result.output)}/>Its props are machine and baseUrl (both required), then mode, theme, accessToken, initialInput, the onReady / onRunStart / onRunComplete / onMessage / onError handlers, and className / style.
@mashin/react also ships an application shell for machines that are a whole app rather than one widget: MashinApp (which is where baseUrl is set), MashinPages, MashinPage (path, optional machine and machineInput), MashinSidebar, MashinNavbar, MashinNavLink. The host address is configured once on MashinApp; the page components take a route, not a host.
import { MashinApp, MashinPages, MashinPage } from "@mashin/react";
<MashinApp baseUrl="https://your-org.mashin.cloud" apiKey={serverSideKey}> <MashinPages> <MashinPage path="/dashboard" machine="dashboard"> <Dashboard /> </MashinPage> </MashinPages></MashinApp>@mashin/vue
Section titled “@mashin/vue”Vue gets the same runtime as a plugin plus composables.
import { createApp } from "vue";import { MashinPlugin } from "@mashin/vue";
createApp(App) .use(MashinPlugin, { baseUrl: "https://your-org.mashin.cloud", accessToken: token }) .mount("#app");<script setup>import { ref } from "vue"import { useMachine } from "@mashin/vue"
const articleBody = ref("")const { run, isLoading, data } = useMachine("summarizer")</script>
<template> <textarea v-model="articleBody"></textarea> <button :disabled="isLoading" @click="run({ text: articleBody })">Summarize</button> <p v-if="data?.output">{{ data.output.summary }}</p></template>@mashin/vue exports MashinPlugin, useMachine, useMachineStatus, useChat, useMashinAuth, and a MashinMachine component that wraps the custom element with machine, baseUrl, mode, theme and accessToken props and ready / run-start / run-complete / message / error events. There is no page or routing shell in the Vue package; for that, use the web component or the React shell.
Others in the repository
Section titled “Others in the repository”@mashin/client is the plain TypeScript client both wrappers are built on. @mashin/next adds server-side helpers (createServerClient, runMachine, createMachineAction) for Next.js, and @mashin/nuxt is a Nuxt 3 module. Same caveat: in the repository, not yet on a registry.
Controlling who can embed
Section titled “Controlling who can embed”Embedding permission belongs to the deployment, not to the machine source. The deployment’s auth_config.allowed_origins drives two headers at once: the CORS Access-Control-Allow-Origin on API calls, and the Content-Security-Policy: frame-ancestors on page responses.
curl -X PATCH https://your-org.mashin.cloud/api/platform/deployments/{deployment_id}/config \ -H "Authorization: Bearer msk_your_deployment_key" \ -H "Content-Type: application/json" \ -d '{"auth_config": {"allowed_origins": ["https://myapp.com", "https://staging.myapp.com"]}}'With that set, only those two origins can frame the page or call the API from a browser. The default is ["*"], which allows any origin, so narrowing it is worth doing before a deployment is public. An empty list falls back to same-origin only.
See also
Section titled “See also”- Surfaces overview - declaring
page,apiand the rest - Surface authentication -
auth,scopes,allow - Pages - the page surface in full
- Computers - where a deployed machine answers