> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.telekesher.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.telekesher.dev/_mcp/server.

# Stop recording room audio

POST https://api.telekesher.dev/api/v1/rooms/{room_id}/playback/record/stop

Stop the active room [Recording](/api/recording). The accepted
[Operation](/api) and the same Recording identity are returned in
one flat response. If the room has no active Recording, the request
returns `409 recording_not_active` without dispatching a recording
command or emitting an event. This operation is not wrapped by
`Idempotency-Key` middleware.

**Triggered webhook:** `recording.ended`, carrying a fresh signed public
URL for the same `recording_uuid`. No post-stop reachability probe is
performed.

Reference: https://docs.telekesher.dev/api/room-playback/stop-recording

## OpenAPI Specification

```yaml
openapi: 3.1.0
info:
  title: voice-api
  version: 1.0.0
paths:
  /api/v1/rooms/{room_id}/playback/record/stop:
    post:
      operationId: stopRoomRecording
      summary: Stop recording room audio
      description: |-
        Stop the active room [Recording](/api/recording). The accepted
        [Operation](/api) and the same Recording identity are returned in
        one flat response. If the room has no active Recording, the request
        returns `409 recording_not_active` without dispatching a recording
        command or emitting an event. This operation is not wrapped by
        `Idempotency-Key` middleware.

        **Triggered webhook:** `recording.ended`, carrying a fresh signed public
        URL for the same `recording_uuid`. No post-stop reachability probe is
        performed.
      tags:
        - roomPlayback
      parameters:
        - name: room_id
          in: path
          description: |-
            [Room](/api/rooms) ID
            (format `{prefix}-room-{uuid}`). The UUID portion is lowercase
            hexadecimal — uppercase hex is rejected with 400.
          required: true
          schema:
            type: string
        - name: Authorization
          in: header
          description: >-
            Application auth. Send `Authorization: Bearer <app_uuid>:<api_key>`.
            See
            [Authentication](https://voice-platform.docs.buildwithfern.com/api/authentication)
            for details.
          required: true
          schema:
            type: string
      responses:
        '202':
          description: Room recording stop accepted
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/Room
                  Playback_stopRoomRecording_Response_202
        '400':
          description: >-
            Missing `Authorization`, malformed Bearer credentials, or invalid
            request parameters. A syntactically valid credential with an invalid
            API key returns `401` instead.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StopRoomRecordingRequestBadRequestError'
        '401':
          description: >-
            The Bearer credential is syntactically valid but its API key is
            invalid. Missing `Authorization` or malformed Bearer credentials
            return `400`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StopRoomRecordingRequestUnauthorizedError'
        '404':
          description: Room not found, including a room owned by another app
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StopRoomRecordingRequestNotFoundError'
        '409':
          description: The room has no active Recording; no command or event is produced.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StopRoomRecordingRequestConflictError'
        '429':
          description: >-
            Rate limit exceeded — a per-command, per-room, or per-app token
            bucket was empty. The `retry_after` field gives the integer seconds
            to wait before retrying.
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/StopRoomRecordingRequestTooManyRequestsError
        '500':
          description: Internal gateway error.
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/StopRoomRecordingRequestInternalServerError
        '503':
          description: The room source node is unavailable, so no stop was dispatched.
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/StopRoomRecordingRequestServiceUnavailableError
servers:
  - url: https://api.telekesher.dev
    description: Gateway server
components:
  schemas:
    ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaStatus:
      type: string
      enum:
        - in_progress
      title: >-
        ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaStatus
    RecordingURLReference:
      type: string
      format: uri
      description: >-
        Opaque, time-bounded absolute HTTPS recording URL. The default validity

        window is 24 hours from the response or event that emits it. Operators

        can configure any positive duration. Fetching does not extend the
        window.
      title: RecordingURLReference
    Room Playback_stopRoomRecording_Response_202:
      type: object
      properties:
        operation_uuid:
          type: string
          format: uuid
        status:
          $ref: >-
            #/components/schemas/ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaStatus
        recording_uuid:
          type: string
          description: Stable opaque identifier for this Recording.
        room_id:
          type: string
          description: Owning [Room](/api/rooms); mutually exclusive with `session_uuid`.
        session_uuid:
          type: string
          description: Owning [Session](/api/sessions); mutually exclusive with `room_id`.
        expires_at:
          type: string
          format: date-time
          description: >-
            Exact expiry instant of pull_url, derived from the URL's signed
            expiration.
        pull_url:
          $ref: '#/components/schemas/RecordingURLReference'
          description: |-
            Fresh whole-Recording download URL. Its configurable validity
            window defaults to 24 hours from this response's emission.
      required:
        - operation_uuid
        - status
        - recording_uuid
        - expires_at
        - pull_url
      description: Flat accepted response for a room Recording stop.
      title: Room Playback_stopRoomRecording_Response_202
    ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaCode:
      type: string
      enum:
        - invalid_request
        - payload_too_large
        - result_set_too_large
        - unauthorized
        - forbidden
        - not_found
        - conflict
        - recording_not_active
        - room_full
        - rate_limited
        - unavailable
        - internal
      description: >-
        Stable, machine-readable error class from a closed set of twelve values.
        Branch on this for programmatic handling. New codes are only added with
        a documented API change. Maps to the HTTP status as follows:
        `invalid_request` (400/415/422), `payload_too_large` and
        `result_set_too_large` (413), `unauthorized` (401), `forbidden` (403),
        `not_found` (404), `recording_not_active` (400); `conflict` and
        `room_full` (409), `rate_limited` (429), `unavailable` (502/503/529),
        `internal` (500).


        Note: `413` is the only status shared by two codes — `payload_too_large`
        means the request body exceeded its size cap, while
        `result_set_too_large` means a list response exceeded the system result
        ceiling (narrow the query with filters and retry).


        Note: `unavailable` is the only code that maps to multiple HTTP
        statuses, which represent different retry conditions: `502` means a
        command dependency could not accept the request, `503` means a required
        service (including the idempotency store) is temporarily unavailable,
        and `529` means the gateway is overloaded with no spare capacity. Use
        the HTTP status and operation description, not just `code`, to
        distinguish them.
      title: >-
        ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaCode
    StopRoomRecordingRequestBadRequestError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: StopRoomRecordingRequestBadRequestError
    StopRoomRecordingRequestUnauthorizedError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: StopRoomRecordingRequestUnauthorizedError
    StopRoomRecordingRequestNotFoundError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: StopRoomRecordingRequestNotFoundError
    StopRoomRecordingRequestConflictError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: StopRoomRecordingRequestConflictError
    StopRoomRecordingRequestTooManyRequestsError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - retry_after
        - success
      title: StopRoomRecordingRequestTooManyRequestsError
    StopRoomRecordingRequestInternalServerError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: StopRoomRecordingRequestInternalServerError
    StopRoomRecordingRequestServiceUnavailableError:
      type: object
      properties:
        code:
          $ref: >-
            #/components/schemas/ApiV1RoomsRoomIdPlaybackRecordStopPostResponsesContentApplicationJsonSchemaCode
          description: >-
            Stable, machine-readable error class from a closed set of twelve
            values. Branch on this for programmatic handling. New codes are only
            added with a documented API change. Maps to the HTTP status as
            follows: `invalid_request` (400/415/422), `payload_too_large` and
            `result_set_too_large` (413), `unauthorized` (401), `forbidden`
            (403), `not_found` (404), `recording_not_active` (400); `conflict`
            and `room_full` (409), `rate_limited` (429), `unavailable`
            (502/503/529), `internal` (500).


            Note: `413` is the only status shared by two codes —
            `payload_too_large` means the request body exceeded its size cap,
            while `result_set_too_large` means a list response exceeded the
            system result ceiling (narrow the query with filters and retry).


            Note: `unavailable` is the only code that maps to multiple HTTP
            statuses, which represent different retry conditions: `502` means a
            command dependency could not accept the request, `503` means a
            required service (including the idempotency store) is temporarily
            unavailable, and `529` means the gateway is overloaded with no spare
            capacity. Use the HTTP status and operation description, not just
            `code`, to distinguish them.
        error:
          type: string
          description: >-
            Human-readable, English-only error message. For display only —
            phrasing may change without notice, so branch on `code`, not on this
            string.
        retry_after:
          type: integer
          description: >-
            Integer seconds to wait before retrying. Present on `429` (the time
            until the denying token bucket refills by one token) and on `529`
            (overload back-off hint). Mirrors the `Retry-After` response header.
        success:
          type: boolean
      required:
        - code
        - error
        - success
      title: StopRoomRecordingRequestServiceUnavailableError
  securitySchemes:
    bearerAppAuth:
      type: http
      scheme: bearer
      description: >-
        Application auth. Send `Authorization: Bearer <app_uuid>:<api_key>`. See
        [Authentication](https://voice-platform.docs.buildwithfern.com/api/authentication)
        for details.

```

## Examples



**Response**

```json
{
  "operation_uuid": "550e8400-e29b-41d4-a716-446655440000",
  "status": "in_progress",
  "recording_uuid": "rec_11111111-2222-4333-8444-555555555555",
  "expires_at": "2026-08-03T12:00:00.000Z",
  "pull_url": "https://recordings.example.com/c/opaque-signed-url-3",
  "room_id": "acW68-room-550e8400-e29b-41d4-a716-446655440000",
  "session_uuid": "acW68-f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
```

**SDK Code**

```python
import requests

url = "https://api.telekesher.dev/api/v1/rooms/room_id/playback/record/stop"

headers = {"Authorization": "Bearer <token>"}

response = requests.post(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.telekesher.dev/api/v1/rooms/room_id/playback/record/stop';
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.telekesher.dev/api/v1/rooms/room_id/playback/record/stop"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.telekesher.dev/api/v1/rooms/room_id/playback/record/stop")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.telekesher.dev/api/v1/rooms/room_id/playback/record/stop")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.telekesher.dev/api/v1/rooms/room_id/playback/record/stop', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.telekesher.dev/api/v1/rooms/room_id/playback/record/stop");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.telekesher.dev/api/v1/rooms/room_id/playback/record/stop")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```