# Pushes

> Part of the Cowliss docs. Markdown for every page: append `index.md` to its URL. Site map: /llms.txt

<!-- GENERATED by apps/docs/scripts/generate-api-reference.ts. Do not edit. Regenerate with `pnpm -F cowliss-docs generate`. -->

A push adds and updates every resource it names and touches nothing it does not name: a key absent from the body stays as the app holds it, an `events` list present replaces the app's and absent leaves it, `purposes` present are upserted. It files a new version of every automation and template whose content changed; a key that did not change gets none. The plan names every resource the push would change whose current content was written in the dashboard or over the API as a conflict, and the push is refused over one unless `force` is set. A push turns nothing on: automation `status` is untouched. Deleting is always an explicit call.

## POST /v1/pushes/plan

What a push of these resources would change

Takes the body `pushes.create` takes, writes nothing, and refuses what that push would refuse. A resource with invalid fields answers `validation_failed` naming each one in `error.issues` by its kind, key and field.

**Auth:** Session token, or an org [pipeline key](/guides/keys/).

**Request body**

```json
{
  "data": {}
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | any | yes |  |

**Response 200**

```json
{
  "data": {
    "appId": "string",
    "entries": [
      {
        "kind": "automation",
        "key": "string",
        "change": "added",
        "fields": [
          "string"
        ]
      }
    ],
    "drift": [
      {
        "kind": "automation",
        "key": "string",
        "reason": "not-in-tree"
      }
    ],
    "conflicts": [
      {
        "kind": "automation",
        "key": "string",
        "updatedAt": "2026-01-15T09:30:00.000Z"
      }
    ],
    "events": {
      "added": [
        "string"
      ],
      "removed": [
        "string"
      ],
      "listed": true
    },
    "purposes": {
      "added": [
        "string"
      ],
      "changed": [
        "string"
      ]
    }
  },
  "meta": {
    "requestId": "string",
    "nextCursor": "string",
    "hasMore": true,
    "warnings": [
      {
        "code": "string",
        "message": "string",
        "suggestion": "string"
      }
    ]
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.appId` | string | yes | length 0–200 |
| `data.entries` | object[] | yes |  |
| `data.entries[]` | object | yes |  |
| `data.entries[].kind` | "automation" \| "segment" \| "template" | yes |  |
| `data.entries[].key` | string | yes |  |
| `data.entries[].change` | "added" \| "changed" \| "unchanged" | yes |  |
| `data.entries[].fields` | string[] | yes |  |
| `data.drift` | object[] | yes |  |
| `data.drift[]` | object | yes |  |
| `data.drift[].kind` | "automation" \| "segment" \| "template" | yes |  |
| `data.drift[].key` | string | yes |  |
| `data.drift[].reason` | "not-in-tree" \| "on-hold" | yes |  |
| `data.conflicts` | object[] | yes |  |
| `data.conflicts[]` | object | yes |  |
| `data.conflicts[].kind` | "automation" \| "segment" \| "template" \| "events" \| "purpose" | yes |  |
| `data.conflicts[].key` | string | yes |  |
| `data.conflicts[].updatedAt` | string (date-time) | yes |  |
| `data.events` | object \| null | yes |  |
| `data.events.added` | string[] | yes |  |
| `data.events.removed` | string[] | yes |  |
| `data.events.listed` | boolean | yes |  |
| `data.purposes` | object \| null | yes |  |
| `data.purposes.added` | string[] | yes |  |
| `data.purposes.changed` | string[] | yes |  |
| `meta` | object | no |  |
| `meta.requestId` | string | no |  |
| `meta.nextCursor` | string \| null | no |  |
| `meta.hasMore` | boolean | no |  |
| `meta.warnings` | object[] | no |  |
| `meta.warnings[]` | object | no |  |
| `meta.warnings[].code` | string | yes |  |
| `meta.warnings[].message` | string | yes |  |
| `meta.warnings[].suggestion` | string | no |  |

Errors follow the [shared envelope and code table](/api-conventions/#errors).

## POST /v1/pushes

Push resources to an app

Applies the automations, templates, segments, event list and purposes in the body to the app in one transaction and files a version of every automation and template whose content changed. A resource the body leaves out is left as it is. Refused with `conflict` when a resource it would change was last written in the dashboard or over the API, unless `force` is true; refused when an automation's `from` address uses a domain your organization has not verified. Resources with invalid fields are refused with `validation_failed`, which names every one in `error.issues` by its kind, key and field, and the first in the message, e.g. `templates["welcome"].template.subject: a subject is required`. A push that prunes (`prune: true`) takes an organization admin's session.

**Auth:** Session token, or an org [pipeline key](/guides/keys/).

**Request body**

```json
{
  "data": {}
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | any | yes |  |

**Response 201**

```json
{
  "data": {
    "appId": "string",
    "versions": [
      {
        "id": "string",
        "orgId": "string",
        "key": "string",
        "kind": "automation",
        "appId": "string",
        "source": "push",
        "moduleDigest": "string",
        "manifest": {
          "key": "string",
          "tags": {},
          "trigger": {
            "event": "string"
          },
          "purpose": "string",
          "enrollment": {
            "cooldown": "string"
          },
          "description": "string",
          "from": "string",
          "definition": {
            "trigger": {
              "event": "string"
            },
            "purpose": "marketing",
            "from": "string",
            "tags": {},
            "description": "string",
            "enrollment": {
              "cooldown": "string"
            },
            "steps": [
              {
                "id": "string",
                "type": "sleep",
                "duration": "string"
              }
            ]
          },
          "spine": [
            {
              "name": "sleep",
              "detail": "string",
              "timeout": "string",
              "steps": [],
              "otherwise": []
            }
          ],
          "bundle": "string"
        },
        "protocol": 42,
        "status": "compiling",
        "pushedAt": "2026-01-15T09:30:00.000Z",
        "compiledAt": "2026-01-15T09:30:00.000Z",
        "error": "string"
      }
    ],
    "prunedSegments": [
      "string"
    ]
  },
  "meta": {
    "requestId": "string",
    "nextCursor": "string",
    "hasMore": true,
    "warnings": [
      {
        "code": "string",
        "message": "string",
        "suggestion": "string"
      }
    ]
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.appId` | string | yes | length 0–200 |
| `data.versions` | object[] | yes |  |
| `data.versions[]` | object | yes |  |
| `data.versions[].id` | string | yes |  |
| `data.versions[].orgId` | string | yes |  |
| `data.versions[].key` | string | yes |  |
| `data.versions[].kind` | "automation" \| "template" | yes |  |
| `data.versions[].appId` | string | yes |  |
| `data.versions[].source` | "push" \| "api" | yes |  |
| `data.versions[].moduleDigest` | string \| null | yes |  |
| `data.versions[].manifest` | object \| object | yes |  |
| `data.versions[].protocol` | integer | yes | -2147483648 ≤ x ≤ 2147483647 |
| `data.versions[].status` | "compiling" \| "ready" \| "failed" | yes |  |
| `data.versions[].pushedAt` | string (date-time) | yes |  |
| `data.versions[].compiledAt` | string (date-time) \| null | yes |  |
| `data.versions[].error` | string \| null | yes |  |
| `data.prunedSegments` | string[] | yes |  |
| `meta` | object | no |  |
| `meta.requestId` | string | no |  |
| `meta.nextCursor` | string \| null | no |  |
| `meta.hasMore` | boolean | no |  |
| `meta.warnings` | object[] | no |  |
| `meta.warnings[]` | object | no |  |
| `meta.warnings[].code` | string | yes |  |
| `meta.warnings[].message` | string | yes |  |
| `meta.warnings[].suggestion` | string | no |  |

Errors follow the [shared envelope and code table](/api-conventions/#errors).
