> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gol.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrate hosted gas

> Owner approval, agent actions, reimbursement, and revocation with the SDK, end to end.

This guide takes one owner account from installation to revocation. Server steps use your API key through `@gol/sdk/server`. Owner steps run in your application's browser code with the owner's own wallet through `@gol/sdk`. GOL never sees the owner's key.

```mermaid theme={null}
sequenceDiagram
  participant App as Your server
  participant Owner as Owner wallet
  participant Agent as Your agent
  participant GOL as GOL API and relayer
  participant Chain as Base Sepolia
  App->>GOL: prepareGasPolicy
  GOL-->>Owner: exact payload to sign
  Owner->>Chain: createMandateWithGas (owner signature)
  App->>GOL: confirmGasPolicy
  App->>GOL: prepareGasExecution
  Agent-->>App: agent signature
  App->>GOL: submitGasExecution
  GOL->>Chain: relayer sends the action, pays gas
  GOL->>Chain: after finality, claim gas from the account within caps
  GOL-->>App: polling and signed webhooks
```

## Before you start

* A test API key with the full integration scopes ([create one](/get-started/test-project)).
* `pnpm add @gol/sdk@next viem`
* An owner account of a [supported type](/guides/accounts) on Base Sepolia, holding the USDC your agent will send and a little ETH for gas reimbursement (a few thousandths of an ETH covers many actions).
* An agent key that your server controls. The agent needs no ETH.

```ts theme={null}
// server.ts
import { GolApiClient } from "@gol/sdk/server";
export const gol = new GolApiClient({ baseUrl: "https://api.gol.network", apiKey: process.env.GOL_API_KEY! });
export const projectId = process.env.GOL_PROJECT_ID!;
const config = await gol.getGasConfiguration(projectId); // core, relayer, attester, limits
```

## 1. Check the owner's account

```ts theme={null}
const status = await gol.getAccountStatus(projectId, account);
// status.family: "safe" | "nexus" | "kernel" | "alchemy" | null
// status.coreInstalled: whether the GOL core is installed on this account
```

`family: null` means the account is not one of the supported configurations. Stop there.

## 2. Install the core (owner, once per account)

The owner installs the GOL core through the account's own owner path. The SDK builds the exact operation; the owner's wallet signs it.

```ts theme={null}
// browser, with a viem wallet client for the owner
import { prepareOwnerInstallation, prepareOwnerOperation, signOwnerOperation } from "@gol/sdk";

const call = await prepareOwnerInstallation(publicClient, family, account, config.core);
const operation = await prepareOwnerOperation(publicClient, family, account, call);
const signature = await signOwnerOperation(walletClient, operation);

if (operation.kind === "safe_transaction") {
  // Any address may submit a Safe transaction signed by its owners.
  const { to, data } = operation.encode(signature);
  await walletClient.sendTransaction({ to, data });
} else {
  // Nexus, Kernel, Alchemy: an EntryPoint v0.7 user operation. Send it directly,
  // or pass tx.userOperation to your bundler.
  const { to, data, gas, userOperation } = operation.encode(signature, ownerAddress);
  await walletClient.sendTransaction({ to, data, gas });
}
```

For a Safe, the owner signs the Safe transaction as typed data. For Nexus, Kernel, and Alchemy the owner signs the user operation hash with `personal_sign`, and the account pays the user operation's gas from its own ETH. Recheck `getAccountStatus` until `coreInstalled` is true.

## 3. Prepare the approval (server)

```ts theme={null}
const now = Math.floor(Date.now() / 1000);
const prepared = await gol.prepareGasPolicy(projectId, {
  account,
  family,
  agent: agentAddress,
  transfer: {
    recipients: [merchant],              // up to 16 addresses
    maxPerActionBaseUnits: "1000000",    // 1 USDC
    maxTotalBaseUnits: "10000000",       // 10 USDC over the mandate's life
  },
  mandate: { validFrom: now - 60, expiresAt: now + 30 * 86400, approvalExpiresAt: now + 3600 },
  gas: {
    maxPerActionWei: config.limits.recommendedPerActionWei,
    maxTotalWei: (BigInt(config.limits.recommendedPerActionWei) * 20n).toString(),
    expiresAt: now + 30 * 86400,
    chargeableOutcomesMask: 1, // 1 success, 2 on-chain refusal, 4 revert
  },
});
```

Preparing creates no authority. `prepared.review` is a plain summary to show the owner: recipients, caps, gas caps, which outcomes they pay gas for, expiry, and the GOL attester and reimbursement recipient. Send `prepared` to the browser.

## 4. Owner signs (browser)

```ts theme={null}
import { signPreparedGasPolicy } from "@gol/sdk";

const approval = await signPreparedGasPolicy(walletClient, prepared, {
  account,
  agent: agentAddress,
  recipients: [merchant],
  maxPerActionBaseUnits: 1_000_000n,
});
const approvalHash = await walletClient.sendTransaction(approval);
```

`signPreparedGasPolicy` first recomputes the policy bytes, mandate and gas policy IDs, digest, and wallet payload from the response and compares them with what you asked for, so a wrong response cannot get signed. The owner sees one `eth_signTypedData_v4` request in the format their account checks. A Safe with several owners needs its threshold of signatures; pass the assembled signature bytes.

## 5. Confirm (server)

```ts theme={null}
import { waitForGasPolicyConfirmation } from "@gol/sdk/server";
const policy = await waitForGasPolicyConfirmation(gol, projectId, prepared.draftId, approvalHash);
// policy.status === "active"; keep policy.gasPolicyId and prepared.authority.mandateId
```

Confirmation returns once both of GOL's independent providers see the approval in a finalized block and the on-chain policy matches the draft. On Base Sepolia this usually takes 20 to 40 minutes.

## 6. Submit an agent action (server and agent)

```ts theme={null}
import { randomBytes } from "node:crypto";

const actionId = `0x${randomBytes(32).toString("hex")}` as const; // your idempotency key
const input = {
  mandateId,
  gasPolicyId,
  recipient: merchant,
  amountBaseUnits: "250000",               // 0.25 USDC
  deadline: Math.floor(Date.now() / 1000) + 600,
};
const { typedData } = await gol.prepareGasExecution(projectId, actionId, input);
const agentSignature = await agentAccount.signTypedData(typedData); // your agent key
const execution = await gol.submitGasExecution(projectId, actionId, { ...input, agentSignature });
```

Store `actionId` before submitting. A retry with the same `actionId` and signature returns the original execution and never moves value twice. Changed fields under the same ID are refused.

## 7. Observe the result

```ts theme={null}
import { waitForGasExecution } from "@gol/sdk/server";
const settled = await waitForGasExecution(gol, projectId, execution.id);
```

| State                                                    | Meaning                                                        |
| -------------------------------------------------------- | -------------------------------------------------------------- |
| `accepted`, `signed`, `submitted`                        | Accepted and being sent by the GOL relayer                     |
| `indeterminate`                                          | Sent, awaiting chain evidence. Never resubmit; GOL resolves it |
| `finalized`                                              | The action is final; the owner's gas share is owed             |
| `claim_signed`, `claim_submitted`, `claim_indeterminate` | GOL is claiming the gas reimbursement                          |
| `collected`                                              | Terminal. Reimbursement collected                              |
| `uncollectable`                                          | Terminal. The claim could not be collected; GOL bears the cost |
| `rejected_preflight`                                     | Terminal. Refused before sending; no value moved               |
| `disputed`                                               | Under review after a dispute                                   |

`settled.receipt` gives the transaction and block, the outcome (`success`, `refusal`, or `revert`), and each fee component. `settled.claimReceipt` gives the reimbursement transaction and the amount collected. `settled.ledger` lists network cost, owner receivable, owner reimbursement collected, and GOL's own costs as separate entries. Use [webhooks](/guides/webhooks) to be notified instead of polling; polling stays authoritative.

An action outside the owner's rules, such as an amount above the per-action cap, is refused by the contract and moves no USDC.

## 8. Revoke

```ts theme={null}
// server
const revocation = await gol.prepareGasPolicyRevocation(projectId, gasPolicyId);
// browser
import { signPreparedRevocation } from "@gol/sdk";
const revoke = await signPreparedRevocation(walletClient, revocation);
const revokeHash = await walletClient.sendTransaction(revoke);
// server
await gol.confirmGasPolicyRevocation(projectId, gasPolicyId, revokeHash); // retry until "confirmed"
```

Revocation takes effect on-chain immediately: the next action and any unpaid claim under the policy fail. The owner does not need GOL for this. `revocation.directOwnerCall` is the same revocation as a call the account makes itself; wrap it with `accountExecuteCall` and `prepareOwnerOperation` to send it through the owner path.

## Costs, trust, and disputes

* GOL's relayer pays each action's gas. After finality GOL claims `gasUsed * effectiveGasPrice + l1Fee + operatorFee` from the account, only for outcomes the owner enabled, and never above the accepted per-action maximum or the remaining total cap. Anything above is GOL's cost, as is the claim transaction's own gas.
* The contract enforces the owner's recipients, caps, gas caps, revocation, and replay protection. It cannot read a past Base receipt, so the GOL attester signs the receipt facts that the platform verified with two independent providers. That signature is a trust dependency bounded by the owner's caps.
* If a charge looks wrong, open a dispute with `openGasExecutionDispute` or from the action's detail panel in the console. New actions under that policy pause until GOL resolves it, and a confirmed overcharge is refunded from GOL's funds.
