# Versioning

> The version marker in a workflow header, and the compatibility promise behind it.

Source: https://www.sendlang.com/docs/reference/versioning
Section: Reference

Every workflow carries a version marker in its header:

**Syntax form, not a complete program:**

```sendflow
workflow "Trial onboarding" v1 { ... }
```

That `v1` is **not** a version of your campaign. It is the version of *SendFlow
itself* — the language profile the file was written against. It exists so that a
file keeps meaning what it meant on the day you wrote it.

## The promise

> A query or workflow that parses and analyzes cleanly under a given profile
> version **means the same thing under every later release carrying that same
> version number**.

That is the whole point of the number. You are not promised that the language
will never change; you are promised that it cannot change *underneath a file you
already wrote* without the number changing too.

SendQL and SendFlow carry their profiles **independently**. SendFlow embeds
SendQL, but it does not inherit its version. If you persist anything, persist
both — a workflow's validity is pinned by the pair.

## What forces a new profile

A bump is required whenever a **previously valid program could change meaning or
stop working**:

- Removing or renaming a production, keyword, operator or reserved word
- Changing what an existing valid program means — precedence, how `within` binds
  after a `where`, how a window is interpreted
- Changing a semantic rule so that previously-clean input now errors, or
  previously-erroring input now passes — **including
  [the absence contract](/docs/sendql/absence)**
- Promoting a warning to an error

## What does not

- **Adding attributes, events, topics, templates or lists.** Your account's
  vocabulary is not part of the language, so it can grow freely.
- Adding grammar that accepts *strictly more* input than before
- Adding new lint **warnings**
- Improving a diagnostic on input that was already invalid

That asymmetry is deliberate. Growing the language is cheap; changing what it
already says is not.

## How the promise is kept

The grammar is not a document somebody remembers to update. It is generated from
the parser and pinned by a golden test: change the parser without changing the
pinned copy and the build fails. It is the same EBNF you are reading on
[the grammar page](/docs/reference/grammar), and it is why that page cannot
quietly go stale.
