# Status callbacks

A status callback is a request Signals sends to your server when a call changes state, for example when it is answered or when it ends. Use status callbacks to react to an outcome, or to record how a call finished, without polling the call resource.

## Where status callbacks come from

| Call | Where you set the URL | Events delivered |
|------|-----------------------|------------------|
| Outbound, created through the Calls API | `StatusCallback` and `StatusCallbackMethod` on create-call | `completed` by default; subscribe to more with `StatusCallbackEvent` |
| Inbound, on a number routed to a voice application | `status_callback_url` and `status_callback_method` on the voice application | `completed` only |

An inbound call on a number that is not routed to a voice application with a status callback URL sends no status callbacks.

### Outbound calls

Set `StatusCallback` (the URL) and, optionally, `StatusCallbackMethod` (`GET` or `POST`, default `POST`) when you create a call. Repeat `StatusCallbackEvent` to subscribe to more events:

```bash
curl -X POST "https://api.signals.pressone.africa/2010-04-01/Accounts/ACCOUNT_SID/Calls.json" \
  -u "ACCOUNT_SID:AUTH_TOKEN" \
  --data-urlencode "To=+2348031234567" \
  --data-urlencode "From=+2342013300100" \
  --data-urlencode "Url=https://voice.example.com/voice/outbound" \
  --data-urlencode "StatusCallback=https://voice.example.com/voice/status-callback" \
  --data-urlencode "StatusCallbackEvent=answered" \
  --data-urlencode "StatusCallbackEvent=completed"
```

### Inbound calls

Set the status callback URL and method on the voice application, in the console or with `PATCH /v1/voice/applications/{application_id}`. The URL must use `https://`, and the URL and method are set or cleared together. Every number routed to the application uses the new values. See the [Programmable Voice API reference](/api/programmable-voice).

## Events

A call moves through up to four lifecycle events, each with an increasing `SequenceNumber`:

| Event | `SequenceNumber` | `CallStatus` |
|-------|------------------|--------------|
| `initiated` | `0` | `queued` |
| `ringing` | `1` | `ringing` |
| `answered` | `2` | `in-progress` |
| `completed` | `3` | `completed`, `busy`, `no-answer`, `canceled` or `failed` |

The `completed` event fires once per call, and its `CallStatus` carries the specific outcome. A call that never rings still reaches `completed`, with `CallStatus` set to `no-answer`, `busy` or `failed`. `StatusCallbackEvent` accepts any string, but only the four event names above ever match.

## Payload

Each status callback carries these parameters:

| Parameter | Description |
|-----------|-------------|
| `CallSid` | Unique identifier for the call. |
| `AccountSid` | Your Voice Account SID. |
| `From` | The number that placed the call. |
| `To` | The number the call was placed to. |
| `CallStatus` | The call state at this event. See the table above. |
| `Direction` | `outbound-api` or `inbound`. |
| `ApiVersion` | Always `2010-04-01`. |
| `Timestamp` | Time of the transition, in RFC 2822 format, for example `Thu, 02 Jul 2026 12:00:50 +0000`. |
| `CallbackSource` | Always `call-progress-events`. |
| `SequenceNumber` | The event's position in the lifecycle. See the table above. |
| `CallDuration` | Duration of the call, in seconds. Present only on the `completed` event. |

## Delivery

If your server does not respond with a 2xx status code, Signals retries the delivery, up to 5 attempts in total. When a call reaches its end through more than one internal transition, for example both legs hanging up, Signals sends a single `completed` delivery.

Status callbacks carry the same `X-Twilio-Signature` header as other webhooks. See [Request signatures](/programmable-voice/webhooks#request-signatures).

Respond with a 2xx status code as soon as you have stored the event. Do slow work, such as calls to other services, after responding.

## Receiver example

The following server logs every transition and responds immediately. It is complete and runnable.

<CodeTabs syncKey="lang">

```javascript title="Node.js"
const express = require("express");
const app = express();
app.use(express.urlencoded({ extended: false }));

app.post("/voice/status-callback", (req, res) => {
  console.log("Call status transition:", {
    callSid: req.body.CallSid,
    callStatus: req.body.CallStatus,
    sequenceNumber: req.body.SequenceNumber,
    timestamp: req.body.Timestamp,
    callDuration: req.body.CallDuration,
  });

  res.sendStatus(200);
});

app.listen(3000);
```

```python title="Python"
from flask import Flask, request

app = Flask(__name__)

@app.route("/voice/status-callback", methods=["POST"])
def status_callback():
    print("Call status transition:", {
        "callSid": request.form.get("CallSid"),
        "callStatus": request.form.get("CallStatus"),
        "sequenceNumber": request.form.get("SequenceNumber"),
        "timestamp": request.form.get("Timestamp"),
        "callDuration": request.form.get("CallDuration"),
    })

    return "", 200

if __name__ == "__main__":
    app.run(port=3000)
```

</CodeTabs>

## Next steps

- [Webhooks](/programmable-voice/webhooks): parameters, transport and call events.
- [Quickstart](/programmable-voice/quickstart): create the outbound call that triggers these events.
- [Calls API reference](/api/voice-calls): the `StatusCallback`, `StatusCallbackMethod` and `StatusCallbackEvent` fields on create-call.
