# Callbacks

Polling a job to know whether it has finished works, but it does not scale and it wastes time. A
**callback** inverts the flow: OpenGate notifies your application over HTTP as the job progresses.

## Enabling callbacks

Set the `callback` field of the job request to the URI you want to be notified on:

```json
{
  "job": {
    "request": {
      "name": "REBOOT_EQUIPMENT",
      "callback": "http://[your_application_address]/[your_URI]",
      "target": { "append": { "entities": ["device_1"] } }
    }
  }
}
```

- The URI follows the [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) format, and only **HTTP**
  transport is supported.
- OpenGate appends the name of the specific callback to this URI when notifying, so a single base URI
  serves both notifications.
- The HTTP method is always `POST`, with the payload as the request body.
- An empty value disables callback notification.

Callbacks work for tasks too: configure `callback` inside `task.job.request`, and every job the task
creates will notify.

## The two notifications

```mermaid
sequenceDiagram
    participant App as Your application
    participant OG as OpenGate
    participant Dev as Devices

    App->>OG: POST /v80/operation/jobs
    OG-->>App: 201 Created + location
    Note over OG: target set resolved,<br>operations created,<br>schedule reached
    OG->>App: POST callback — job started
    OG->>Dev: operations dispatched
    Dev-->>OG: results per entity
    Note over OG: all operations finished,<br>cancelled or timed out
    OG->>App: POST callback — job finished
```

| Callback | Fired when | Payload carries |
|---|---|---|
| **Started** | The job begins executing — immediately, or when its schedule says so. | `id`, `request`, `report.execution` |
| **Finished** | The job is over: schedule terminated, job cancelled, or all operations completed. | `id`, `request`, `report.execution`, `report.summary`, `result` with the first page of per-entity operations |

Creating a job is not itself notified: the `201 Created` response to your `POST` already tells you the
job exists, and `report.summary` is available from `GET /v80/operation/jobs/{jobId}` from that moment
on.

## Job started callback

Fired when execution actually begins. For a scheduled job this happens when the scheduling parameters
say so, which may be long after creation:

```json
{
  "job": {
    "id": "33eb9dfa-7a87-41f7-9bad-7b5a26712fec",
    "request": {
      "name": "REBOOT_EQUIPMENT",
      "parameters": {
        "TYPE": "HARDWARE"
      },
      "notify": true,
      "user": "user@mail.com"
    },
    "report": {
      "execution": {
        "activatedDate": "2010-12-20T10:10:00.00Z",
        "startedDate": "2010-12-20T10:10:00.00Z",
        "finishedDate": ""
      }
    }
  }
}
```

## Job finished callback

The most complete of the three. Besides the summary counters it includes the **first page** of
per-entity results, so a small job needs no follow-up request at all. For larger jobs, page through
the remaining results with `GET /v80/operation/jobs/{jobId}/operations`.

```json
{
  "job": {
    "id": "33eb9dfa-7a87-41f7-9bad-7b5a26712fec",
    "request": {
      "name": "REBOOT_EQUIPMENT",
      "parameters": {
        "TYPE": "HARDWARE"
      },
      "notify": true,
      "user": "user@mail.com"
    },
    "report": {
      "execution": {
        "activatedDate": "2014-03-12T11:43:35Z",
        "startedDate": "2014-03-12T11:43:35Z",
        "finishedDate": "2014-03-12T11:44:52Z"
      },
      "summary": {
        "status": "FINISHED",
        "total": 3,
        "inProgress": {
          "total": 0,
          "scheduled": 0,
          "pendingExecution": 0,
          "waitingForConnection": 0,
          "started": 0
        },
        "finished": {
          "total": 2,
          "successful": 1,
          "error": 0,
          "cancelled": {
            "total": 1,
            "byEngine": 0,
            "byUser": 0,
            "byTimeout": 1,
            "byExternalTimeout": 0,
            "byExternal": 0
          }
        },
        "finishedOutOfTime": {
          "total": 1,
          "successful": 1,
          "error": 0
        }
      }
    },
    "result": {
      "page": {
        "number": 1,
        "of": 50
      },
      "operations": [
        {
          "operationId": "86dd3409-6fcd-49d4-be6b-b2fa497207ec",
          "entityId": "device_1",
          "resourceType": "entity.device",
          "name": "REBOOT_EQUIPMENT",
          "parameters": {
            "TYPE": "HARDWARE"
          },
          "notify": true,
          "execution": {
            "activatedDate": "2014-10-01T09:03:42Z",
            "startedDate": "2014-10-01T09:03:45Z",
            "finishedDate": "2014-10-01T09:04:45Z"
          },
          "user": "user@mail.com",
          "status": "FINISHED",
          "result": "SUCCESSFUL",
          "description": "successful operation",
          "steps": [
            {
              "name": "RESET",
              "result": "SUCCESSFUL",
              "description": "Reset ok",
              "timestamp": "2012-09-27T16:46:02.10Z"
            }
          ]
        },
        {
          "operationId": "29891afd-5f4f-4b23-800e-586bd4ecb0eb",
          "entityId": "device_2",
          "resourceType": "entity.device",
          "name": "REBOOT_EQUIPMENT",
          "parameters": {
            "TYPE": "HARDWARE"
          },
          "notify": true,
          "execution": {
            "activatedDate": "2014-10-01T09:03:42Z",
            "startedDate": "2014-10-01T09:03:45Z",
            "finishedDate": "2014-10-01T09:04:45Z"
          },
          "user": "user@mail.com",
          "status": "FINISHED",
          "result": "SUCCESSFUL",
          "description": "successful operation",
          "steps": [
            {
              "name": "RESET",
              "result": "SUCCESSFUL",
              "description": "Reset ok",
              "timestamp": "2012-09-27T16:46:02.10Z"
            }
          ]
        }
      ]
    }
  }
}
```

Note that a job reaching the finished callback is not necessarily a job that succeeded: in this
example `status` is `FINISHED`, but of the three operations one was cancelled by timeout and one
finished out of time. Always read the counters, not just the status. See the
[status reference](../status_reference/) for what each value means.

## Notifications versus callbacks

`callback` and `notify` are different mechanisms and can be used together:

| Field | Recipient | Purpose |
|---|---|---|
| `callback` | Your application, over HTTP | Machine-to-machine job progress notification |
| `notify` | The platform's notification channels (email, trap) | Human notification of the operation result |
