# Exporting Rules for Deployment

Turn your L4 rules into live REST API endpoints on [Legalese Cloud](https://legalese.cloud) that applications and AI agents can call.

**Prerequisites:** Basic L4 knowledge ([Your First L4 File](/l4/tutorials/getting-started/first-l4-file.md))

---

## What You'll Build

An insurance premium calculator with two exported rules: one to compute a premium and one to check discount eligibility. You will deploy them from VS Code to the Legalese Cloud and call them as a REST API.

**Complete example:** 

```l4-file
-- insurance-premium.l4
-- Example: Exporting functions for deployment

§ `Insurance Premium Calculator`

DECLARE Applicant
    HAS `age` IS A NUMBER
        `risk score` IS A NUMBER
        `is existing customer` IS A BOOLEAN

-- The primary function, marked as the default export
@export default Calculate the insurance premium for an applicant
GIVEN applicant IS A Applicant @desc The applicant's details
GIVETH A NUMBER
DECIDE `calculate premium` IS
    IF applicant's `risk score` > 0.7
    THEN applicant's `age` * 100
    ELSE IF applicant's `is existing customer`
         THEN applicant's `age` * 40
         ELSE applicant's `age` * 60

-- A secondary exported function
@export Check whether the applicant qualifies for a discount
GIVEN applicant IS A Applicant @desc The applicant to check
GIVETH A BOOLEAN
DECIDE `qualifies for discount` IF
    applicant's `is existing customer`
    AND applicant's `risk score` <= 0.5

-- Internal helper — not exported (no @export annotation)
GIVEN applicant IS A Applicant
GIVETH A NUMBER
`base rate` applicant MEANS
    IF applicant's `age` < 30 THEN 50 ELSE 80

-- Test data
`test applicant` MEANS Applicant 35 0.4 TRUE

#EVAL `calculate premium` `test applicant`
#EVAL `qualifies for discount` `test applicant`
```



---

## Step 1: Mark Rules with `@export`

The `@export` annotation tells L4 which rules should be exposed as API endpoints. Place it directly above a `DECIDE` or `MEANS` definition:

```l4
@export default Calculate the insurance premium for an applicant
GIVEN applicant IS A Applicant @desc The applicant's details
GIVETH A NUMBER
DECIDE `calculate premium` IS
    IF applicant's `risk score` > 0.7
    THEN applicant's `age` * 100
    ELSE applicant's `age` * 60
```

Without `@export`, a rule stays internal — only usable within the file, never exposed to the outside world.

### The `@export` annotation for rules explained

| Syntax                          | Meaning                                                              |
| ------------------------------- | -------------------------------------------------------------------- |
| `@export <description>`         | Export this rule with a human-readable description                   |
| `@export default <description>` | Export as the **default** rule (used when no rule name is specified) |
| `@desc <description>`           | Internal description only — **not** exported                         |

The description text becomes the rule's documentation in the generated API schema. Use it above the GIVEN/GIVETH parameter or return type definitions.

### Parameter descriptions with `@desc`

Add inline `@desc` annotations to `GIVEN` parameters so API consumers can learn what each input means:

```l4
@export Check whether the applicant qualifies for a discount
GIVEN applicant IS A Applicant @desc The applicant to check
GIVETH A BOOLEAN
DECIDE `qualifies for discount` IF
    applicant's `is existing customer`
    AND applicant's `risk score` <= 0.5
```

---

## Step 2: Verify Locally

Before deploying, make sure your file compiles and the exports look right.

### In VS Code

1. Open your `.l4` file
2. Open the **L4 sidebar** panel
3. Click the **Deploy** tab

The Deploy tab shows a preview of all exported rules discovered in the active file — their names, descriptions, parameters, and return types. If the list is empty, your file either has no `@export` annotations or contains errors.

---

## Step 3: Deploy from VS Code

1. Make sure your extension is properly connected to your free [Legalese Cloud](https://legalese.cloud) account.
2. Verify the exported rules preview looks correct
3. Click **Deploy**
4. Enter a **deployment name** (e.g. `insurance-premium`) — this becomes part of the API URL. The name must be 1–36 characters, using only letters, numbers, hyphens, and underscores.
5. Click **Deploy Now**

The extension uploads your file to the L4 service, which compiles it and makes the rules available as REST endpoints.

### Deployment status

After deploying, the status progresses through:

| Status        | Meaning                                     |
| ------------- | ------------------------------------------- |
| **Pending**   | Uploaded, waiting to compile                |
| **Compiling** | Being compiled by the service               |
| **Ready**     | Live and accepting requests                 |
| **Failed**    | Compilation error — check the error message |

### Updating an existing deployment

When you deploy to a name that already exists, the extension checks for **breaking changes** — such as removed parameters, changed return types, or renamed rules. If breaking changes are detected, you will see a warning before confirming the update.

---

## Step 4: Manage Deployments

Switch to the **Deployments** tab in the sidebar to see all active deployments. Each deployment shows:

- The deployment name
- The number of exported rules
- Each rule's name, description, and parameters

From here you can:

- **Expand/collapse** deployments to inspect their rules
- **Undeploy** a deployment to remove it (with a confirmation prompt, since this breaks existing integrations)

---

## Step 5: Call the API

Once a deployment is **Ready**, its rules are available as REST endpoints at `https://api.legalese.cloud/{orgSlug}/{deploymentId}/...`, where `{orgSlug}` is your Legalese Cloud organization and `{deploymentId}` is the deployment name you chose. Authenticate with an API key from the [Legalese Cloud console](https://legalese.cloud) (`Authorization: Bearer sk_...`).

Note that rule and parameter names are **sanitized** for the API: spaces and other special characters become hyphens, so the rule `qualifies for discount` is called as `qualifies-for-discount` and the field `risk score` is passed as `"risk-score"`.

### List available rules

Here are some ways you can use the REST API from your command line:

```bash
curl https://api.legalese.cloud/{orgSlug}/insurance-premium/functions \
  -H "Authorization: Bearer sk_..."
```

### Evaluate the default rule

```bash
curl -X POST https://api.legalese.cloud/{orgSlug}/insurance-premium/functions/calculate-premium/evaluation \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "arguments": {
      "applicant": {
        "age": 35,
        "risk-score": 0.4,
        "is-existing-customer": true
      }
    }
  }'
```

### Evaluate a specific rule

```bash
curl -X POST https://api.legalese.cloud/{orgSlug}/insurance-premium/functions/qualifies-for-discount/evaluation \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "arguments": {
      "applicant": {
        "age": 35,
        "risk-score": 0.4,
        "is-existing-customer": true
      }
    }
  }'
```

### Reading the answer

The answer comes back as JSON, in `value`, under `result`.
For the applicant above, `calculate-premium` returns:

```json
{ "contents": { "result": { "value": 1400 } }, "tag": "SimpleResponse" }
```

and `qualifies-for-discount` returns:

```json
{ "contents": { "result": { "value": true } }, "tag": "SimpleResponse" }
```

What `value` holds depends on what the rule gives back:

| The rule gives back                                   | `value` is                                                                                                                  |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `TRUE` or `FALSE`                                     | `true` or `false`                                                                                                           |
| a number                                              | that number; one that is not whole comes back as a decimal, to about 16 significant digits: a third is `0.3333333333333333` |
| a string                                              | that string                                                                                                                 |
| a `DATE`                                              | the date as text, year first: `"2026-01-31"`                                                                                |
| `NOTHING`, from a `MAYBE`                             | `null`                                                                                                                      |
| `JUST` a value, from a `MAYBE`                        | the value itself, with nothing around it                                                                                    |
| a list                                                | a JSON array, even when the list has only one item                                                                          |
| one of an enum's names, such as `` `under review` ``  | the name as a string, without backticks: `"under review"`                                                                   |
| a record, such as a `Pair` of `left` and `right side` | an object with one key, the record's name, holding its fields: `{"Pair": {"left": 5, "right side": 6}}`                     |

Four things to know when you read an answer:

- The field names inside an answer are spelled as the rule spells them, spaces included; they are not turned into hyphens.
- A rule that gives back a `MAYBE` of a `MAYBE` answers `null` both for `NOTHING` and for `JUST NOTHING`, so the two cannot be told apart.
- Each published rule comes with a description of its answer, its `returnSchema`.
  That description does not yet show the record's name around a record's fields, or that a `MAYBE` answer can be `null`.
  Go by the table above, not by the `returnSchema`, for those two.
- A long list can come back cut short.
  A list answer of around 200 items or more may end with two `null`s in place of the rest, still with status 200, so two `null`s at the end of a long list mean it was cut, not that items were empty.

### Interactive query plans

Not sure which inputs matter? Use the query-plan endpoint to ask only the questions that affect the outcome:

```bash
curl -X POST https://api.legalese.cloud/{orgSlug}/insurance-premium/functions/calculate-premium/query-plan \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{"arguments": {"applicant": {"is-existing-customer": true}}}'
```

This returns which inputs are still needed, ranked by their impact on the result.

---

## Step 6: Expose to AI Agents

Once deployed, your L4 rules are automatically available to AI agents through two complementary protocols: **MCP** for server-side tool use and **WebMCP** for browser-based agents.

### MCP (Model Context Protocol)

The L4 service includes a built-in [MCP](https://modelcontextprotocol.io/) server that exposes your deployed rules as structured tools over JSON-RPC 2.0. LLM tool-use clients, agent frameworks, and IDE extensions can connect to it directly — no browser required.

**Discover available tools:**

```bash
curl -X POST https://mcp.legalese.cloud/{orgSlug}/insurance-premium \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

**Call a tool:**

```bash
curl -X POST https://mcp.legalese.cloud/{orgSlug}/insurance-premium \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "calculate-premium",
      "arguments": {
        "applicant": {
          "age": 35,
          "risk-score": 0.4,
          "is-existing-customer": true
        }
      }
    }
  }'
```

This endpoint is scoped to one deployment. There is also an org-wide rules MCP at `https://mcp.legalese.cloud` (no org in the URL — your organization is resolved from your sign-in); see [Use Deployed Rules from an AI Agent](/l4/tutorials/legalese-cloud/agent-marketplace.md). For MCP client setup, see the [MCP Server](/l4/tutorials/legalese-cloud/mcp-server.md) tutorial.

The service logs every MCP tool call as structured JSON — the same format used for REST API requests — so you can monitor agent activity in your log aggregator alongside regular API traffic.

### WebMCP (Browser AI Agents)

[WebMCP](https://webmachinelearning.github.io/webmcp/) is a W3C standard that lets websites expose structured tools to browser-based AI agents — no scraping or DOM inspection required.

The L4 service provides a JavaScript snippet you can embed on any web page:

```html
<!-- Expose all deployed rules as WebMCP tools -->
<script
  src="https://api.legalese.cloud/.webmcp/embed.js"
  data-org="{orgSlug}"
  data-api-key="sk_..."
></script>

<!-- Scope to specific deployments -->
<script
  src="https://api.legalese.cloud/.webmcp/embed.js"
  data-org="{orgSlug}"
  data-api-key="sk_..."
  data-scope="insurance-premium/*"
></script>
```

By default (`data-tools="auto"`) the script registers one tool per exported rule when the scope covers at most 10 rules; with more than 10, it registers discovery tools (`search_rules`, `get_rule_schema`, `evaluate_rule`) that browser AI agents call to find and invoke your L4 rules. See the [WebMCP Embed Script](/l4/tutorials/legalese-cloud/webmcp-embed.md) tutorial for all attributes and how to get an API key.

---

## Summary

| Step     | What you do                                          |
| -------- | ---------------------------------------------------- |
| Annotate | Add `@export` (and optionally `default`) above rules |
| Describe | Add `@desc` to parameters for API documentation      |
| Preview  | Check the Deploy tab in VS Code                      |
| Deploy   | Click Deploy, choose a name, confirm                 |
| Manage   | Use the Deployments tab to inspect and undeploy      |
| Call     | Use the REST API, MCP, or embed WebMCP on a web page |

---

## Common Mistakes

### Forgetting `@export`

```l4
-- ❌ No @export — this rule won't be deployed
GIVEN x IS A NUMBER
GIVETH A NUMBER
squared x MEANS x * x

-- ✅ With @export — available via the API
@export Calculate the square of a number
GIVEN x IS A NUMBER @desc The input number
GIVETH A NUMBER
squared x MEANS x * x
```

### Placing `@export` in the wrong position

`@export` must appear directly above the rule definition (before `GIVEN` or the rule name), not between `GIVETH` and `DECIDE`:

```l4
-- ❌ Wrong — @export between GIVETH and DECIDE
GIVEN x IS A NUMBER
GIVETH A NUMBER
@export Square a number
squared x MEANS x * x

-- ✅ Right — @export before the rule
@export Square a number
GIVEN x IS A NUMBER
GIVETH A NUMBER
squared x MEANS x * x
```

### Missing parameter descriptions

Without `@desc` on parameters, API consumers won't know what inputs mean. Always describe your parameters:

```l4
-- ❌ No parameter descriptions
@export Calculate premium
GIVEN applicant IS A Applicant
GIVETH A NUMBER
DECIDE `calculate premium` IS ...

-- ✅ With parameter descriptions
@export Calculate premium
GIVEN applicant IS A Applicant @desc The applicant's details
GIVETH A NUMBER
DECIDE `calculate premium` IS ...
```

---

## Self-hosted jl4-service

If you run your own `jl4-service` instead of Legalese Cloud, the same API surface is served from your own host (e.g. `http://localhost:8080`), with the deployment id in the path instead of the org/deployment pair:

- `GET /deployments/{deploymentId}/functions` — list exported rules
- `POST /deployments/{deploymentId}/functions/{name}/evaluation` — evaluate a rule
- `POST /deployments/{deploymentId}/functions/{name}/query-plan` — interactive query plan
- `POST /deployments/{deploymentId}/.mcp` (or `/{deploymentId}/.mcp`) — deployment-scoped MCP; `/.mcp` is the org-wide endpoint
- `GET /.webmcp/embed.js` — the WebMCP embed script
- `GET /deployments/{deploymentId}/openapi.json` — the deployment's OpenAPI spec (`/openapi.json` covers all deployments)

---

## Next Steps

- [Common Patterns](/l4/reference/patterns/common-patterns.md) — Frequently used L4 patterns
- [Reference: Annotations](/l4/reference/GLOSSARY.md) — All L4 annotations including `@export` and `@desc`
