> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lusor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Set a Lusor Phone's location and timezone

> Sets the GPS location and timezone the phone reports, replacing the
automatic match to its Internet exit IP. The setting persists across
restarts and IP changes until it is cleared with `DELETE /geo`, or
until the phone is wiped (Wipe & reset) or removed. It can
disagree with the exit IP's country or timezone; that is allowed.

The change is stored at once and given to the phone in the
background (usually within a minute); read `apply.state` with
`GET /geo`. Allowed for an API key on its own workspace and for
workspace admins and members.




## OpenAPI

````yaml /api-reference/openapi.yaml put /v1/workspaces/{workspaceId}/devices/{deviceId}/geo
openapi: 3.1.0
info:
  title: Lusor API
  version: 0.1.0
  description: |
    Run AI agents on real phones, or drive a phone directly with
    deterministic actions.

    ## Authentication

    Every `/v1/*` endpoint except `GET /v1/health` requires an API key.
    Create one in the Lusor dashboard under **Settings → API Keys**. A key
    is shown once and is bound to one workspace. Revoking it there stops it
    working immediately.

    Send it in either header:

    - `X-Api-Key: <your API key>`
    - `Authorization: Bearer <your API key>`

    Because a key is bound to its workspace, `workspaceId` query and body
    parameters are optional for API-key callers. A request that names a
    different workspace returns an opaque `404`.

    ## Runs

    Use `POST /v1/agents/{id}/turns` to start or continue an agent run.
    Read the run under `/v1/sessions/{id}`; poll
    `GET /v1/sessions/{id}/status` or `GET /v1/sessions/{id}/events` for
    progress.

    ## Errors

    Errors are JSON: `{ "error": "<code>", "message": "<text>" }`. A
    request that fails validation returns `400` in one of two shapes,
    depending on the endpoint: `{ "error": "validation_error", "details":
    { ... } }` (direct phone control actions, for example), or
    `{ "error": "bad_request", "message": "<text>" }` (agent endpoints, for
    example). Branch on `error`.
  contact:
    name: Lusor
    url: https://lusor.ai
  license:
    name: Proprietary
    url: https://lusor.ai/terms
servers:
  - url: https://api.lusor.ai
    description: Production
security:
  - ApiKey: []
  - BearerAuth: []
tags:
  - name: Agents
    description: Persistent agents, the phone each one uses, and their turns (runs).
  - name: Secrets
    description: |
      Secrets saved in a workspace (passwords, sign-in details). A run uses
      them by name through `secrets`; values never travel in a request.
  - name: Sessions
    description: |
      Run records: list, inspect, stop, and read events and artifacts. Start
      a run with `POST /v1/agents/{id}/turns`, or without an agent through
      `POST /v1/workspaces/{workspaceId}/devices/{deviceId}/sessions`.
  - name: LiveView
    description: |
      View-only WebRTC live view of a session's phone: signaling, ICE
      servers, and stream control. API keys get live view only on the
      customer's own phone, not on a Lusor Phone.
  - name: Devices
    description: |
      The phones in your workspace, and reads of their current screen and
      UI tree.
  - name: DeviceVerbs
    description: |
      Direct phone control: deterministic actions (tap, swipe, type, key,
      clear, back, home, recent, screenshot, apps/start, apps/stop) and
      chains of them. On a Lusor Phone, API-key calls meter phone minutes.
  - name: Shell
    description: |
      Restricted shell escape hatch. Commands are checked against a
      denylist and audit-logged. No command output is returned.
  - name: Datasets
    description: Lists a run produced, saved as reusable Datasets.
  - name: Skills
    description: Reusable automation skills (Markdown documents).
  - name: PhoneNumbers
    description: |
      Workspace phone numbers and their stored SMS messages. Read-only for
      API keys.
  - name: Emails
    description: |
      Workspace Agent Mail addresses and stored inbound mail.
      Read-only: there is no compose, reply, or send endpoint, and message
      bodies are returned as raw plain text without HTML execution, remote
      asset fetches, or code extraction.
  - name: Workspaces
    description: The workspace an API key is bound to.
  - name: Catalog
    description: The catalog of apps that can be installed on a Lusor Phone.
  - name: Health
    description: Service health check.
paths:
  /v1/workspaces/{workspaceId}/devices/{deviceId}/geo:
    put:
      tags:
        - Devices
      summary: Set a Lusor Phone's location and timezone
      description: |
        Sets the GPS location and timezone the phone reports, replacing the
        automatic match to its Internet exit IP. The setting persists across
        restarts and IP changes until it is cleared with `DELETE /geo`, or
        until the phone is wiped (Wipe & reset) or removed. It can
        disagree with the exit IP's country or timezone; that is allowed.

        The change is stored at once and given to the phone in the
        background (usually within a minute); read `apply.state` with
        `GET /geo`. Allowed for an API key on its own workspace and for
        workspace admins and members.
      operationId: setDeviceGeo
      parameters:
        - $ref: '#/components/parameters/WorkspaceIdPath'
        - $ref: '#/components/parameters/DeviceIdPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeviceGeoInput'
      responses:
        '200':
          description: The stored setting
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/DeviceGeo'
                required:
                  - data
        '400':
          description: |
            `geo_override_invalid`: latitude, longitude or timezone is out
            of range or not an IANA zone name.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/DeviceGeoConflict'
        '502':
          $ref: '#/components/responses/UpstreamError'
        '504':
          $ref: '#/components/responses/DeviceGeoQueued'
components:
  parameters:
    WorkspaceIdPath:
      name: workspaceId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Workspace UUID. Caller must have access.
    DeviceIdPath:
      name: deviceId
      in: path
      required: true
      schema:
        type: string
      description: Device id (paired into the workspace).
  schemas:
    DeviceGeoInput:
      type: object
      additionalProperties: false
      properties:
        latitude:
          type: number
          minimum: -90
          maximum: 90
          description: Degrees, WGS84. Stored to six decimals.
        longitude:
          type: number
          minimum: -180
          maximum: 180
          description: Degrees, WGS84. Stored to six decimals.
        timezone:
          type: string
          maxLength: 64
          description: IANA zone name, for example `Europe/Paris`.
      required:
        - latitude
        - longitude
        - timezone
    DeviceGeo:
      type: object
      properties:
        mode:
          type: string
          enum:
            - auto
            - override
          description: >-
            `auto` follows the Internet exit IP; `override` is a value set with
            `PUT /geo`.
        latitude:
          type:
            - number
            - 'null'
          description: The latitude the phone was last given; null when none is known yet.
        longitude:
          type:
            - number
            - 'null'
        timezone:
          type:
            - string
            - 'null'
        apply:
          type:
            - object
            - 'null'
          description: Override mode only. Whether the phone has been given the override.
          properties:
            state:
              type: string
              enum:
                - pending
                - applied
                - held
                - failed
            reason:
              type: string
              description: >-
                `held` only: `lifecycle` (restarting or resetting; applied when
                it finishes) or `no_pad`.
            error:
              type: string
              description: '`failed` only: `geo_apply_failed`. Send the PUT again to retry.'
          required:
            - state
      required:
        - mode
        - latitude
        - longitude
        - timezone
        - apply
    ErrorResponse:
      type: object
      required:
        - error
        - message
      properties:
        error:
          type: string
        message:
          type: string
  responses:
    Unauthorized:
      description: Authentication required or invalid credentials.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: The caller may not perform this action.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    DeviceGeoConflict:
      description: >
        `error` is one of:

        `not_managed_phone` (the device is not a Lusor Phone on the Phone plan;
        trial phones included),

        `phone_busy` (the phone is restarting or resetting: try again when it is
        ready),

        or `phone_not_ready` (the phone is not set up yet, or is removed).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    UpstreamError:
      description: The phone or a Lusor service could not be reached.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    DeviceGeoQueued:
      description: |
        `geo_change_queued`: another operation on the phone is running. The
        change is queued and still applies; read it back with `GET /geo`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: X-Api-Key
      description: Workspace API key, created in the dashboard under Settings → API Keys.
    BearerAuth:
      type: http
      scheme: bearer
      description: >-
        The same workspace API key, sent as `Authorization: Bearer <your API
        key>`.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.