# Ownership Transfers

https://dev.wisecp.com/es/ownership-transfers

The four endpoints behind the requests that move a service to another client.

## Overview

A client can hand a service over to another client. These four endpoints read those requests and settle them; approval moves the service to the target client.

The id in the paths is the **request's**, not the service's. Requests are kept as event records, so they carry a number of their own.

This is **not a licence transfer**. Moving a licence from one installation to another is a separate system with its own endpoints.

## Reference

### Listing the Requests

get/api/v1/admin/services/transfer-requests

`Services/GetTransferRequests` admin paged

Returns the service handover requests, with the waiting ones first.

Query parameters 7

searchstringSearches the name, e-mail, company and service name on either side.

statusstring`pending` ya da `approved`.

from_idintFilters by the client handing over.

to_idintFilters by the client taking on.

service_idintFilters by service.

pageintDefaults to 1.

limitintDefaults to 25, maximum 100.

Response fields data[] — 8

idintId of the request. This is an event id, **not** a service id, and it is what the other endpoints expect.

service_idintId of the service being handed over.

statusstring`pending` is waiting, `approved` has gone through.

reasonstringWhy the transfer was asked for.

created_atstringWhen the request was opened.

serviceobject 3 fieldsA summary of the service.

idintService id.

namestringThe service name.

typestringThe service type.

fromobject 4 fieldsThe client handing it over.

idintClient id.

full_namestringFirst and last name.

company_namestringCompany name.

emailstringE-mail address.

toobject 4 fieldsThe client taking it on.

idintClient id.

full_namestringFirst and last name.

company_namestringCompany name.

emailstringE-mail address.

Meta 4

totalintTotal records matching the filter.

pageintThe page you are on.

limitintThe page size.

next_pageintThe next page. Zero means you are on the last one.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -G 'https://panel.example.com/api/v1/admin/services/transfer-requests' \
  -H "Authorization: Bearer $API_KEY" \
  -d status=pending
```

```javascript
const url = new URL('https://panel.example.com/api/v1/admin/services/transfer-requests');
url.searchParams.set('status', 'pending');

const res  = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();
```

```php
$url = 'https://panel.example.com/api/v1/admin/services/transfer-requests?' . http_build_query(['status' => 'pending']);

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$response = Api::Services()->GetTransferRequests([], ['status' => 'pending']);
```

Response 200

```json
{
  "data": [
    {
      "id": 7382,
      "service_id": 558,
      "status": "pending",
      "reason": "Sold to another account",
      "created_at": "2026-06-21 18:00:00",
      "service": { "id": 558, "name": "example.com", "type": "hosting" },
      "from": {
        "id": 88,
        "full_name": "John Doe",
        "company_name": "",
        "email": "john@example.com"
      },
      "to": {
        "id": 89,
        "full_name": "Jane Doe",
        "company_name": "",
        "email": "jane@example.com"
      }
    }
  ],
  "meta": { "total": 1, "page": 1, "limit": 25, "next_page": 0 }
}
```

### Request Detail

get/api/v1/admin/services/transfer-requests/{eid}

`Services/GetTransferRequest` admin request id

Returns one handover request, with the approval details when it has gone through.

Response fields data — 9

idintId of the request. This is an event id, **not** a service id, and it is what the other endpoints expect.

service_idintId of the service being handed over.

statusstring`pending` is waiting, `approved` has gone through.

reasonstringWhy the transfer was asked for.

created_atstringWhen the request was opened.

serviceobject 3 fieldsA summary of the service.

idintService id.

namestringThe service name.

typestringThe service type.

fromobject 4 fieldsThe client handing it over.

idintClient id.

full_namestringFirst and last name.

company_namestringCompany name.

emailstringE-mail address.

toobject 4 fieldsThe client taking it on.

idintClient id.

full_namestringFirst and last name.

company_namestringCompany name.

emailstringE-mail address.

approvedobject | nullThe approval: who approved it, their name and when. Empty while the request is still waiting.

Errors 2

not_found404No such transfer request.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/admin/services/transfer-requests/7382' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res  = await fetch('https://panel.example.com/api/v1/admin/services/transfer-requests/7382', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/services/transfer-requests/7382');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// The id in the path is the REQUEST'S id; passing a service id answers 404.
$response = Api::Services()->GetTransferRequest(['eid' => 7382]);
```

### Approving a Request

post/api/v1/admin/services/transfer-requests/{eid}/approve

`Services/ApproveTransferRequest` admin cannot be undone

Moves the service to the target client and marks the request approved.

Body —

——No body is needed. The target client is the one named in the stored request and cannot be changed at approval time; send an empty body.

Response fields data — 5

statusstringThe status afterwards.

idintId of the request.

service_idintId of the service handed over.

old_owner_idintId of the previous owner.

new_owner_idintId of the new owner.

Errors 6

not_found404No such request or service.

not_pending422The request has already been approved.

invalid_target422The request carries no target client.

target_not_found422The target client was not found.

blocked_by_gate422The `gate:service.transfer_approve` hook vetoed the operation. One of your own addons may be blocking the handover.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X POST 'https://panel.example.com/api/v1/admin/services/transfer-requests/7382/approve' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/services/transfer-requests/7382/approve', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/services/transfer-requests/7382/approve');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Approval is one way: undoing it means opening a NEW request in the other direction.
$response = Api::Services()->ApproveTransferRequest(['eid' => 7382]);

$movedFrom = $response['data']['old_owner_id'];
$movedTo   = $response['data']['new_owner_id'];
```

Response 200 422

```json
{
  "data": {
    "status": "approved",
    "id": 7382,
    "service_id": 558,
    "old_owner_id": 88,
    "new_owner_id": 89
  }
}
```

```json
{
  "error": {
    "code": "not_pending",
    "message": "Transfer request is not pending."
  }
}
```

### Deleting a Request

delete/api/v1/admin/services/transfer-requests/{eid}

`Services/DeleteTransferRequest` admin

Deletes the request record. It does not touch who owns the service.

Response fields data — 2

deletedboolWhether the delete succeeded.

idintId of the deleted request.

Errors 2

not_found404No such transfer request.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X DELETE 'https://panel.example.com/api/v1/admin/services/transfer-requests/7382' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/services/transfer-requests/7382', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/services/transfer-requests/7382');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Deleting an approved request does NOT undo the handover; only the record goes.
$response = Api::Services()->DeleteTransferRequest(['eid' => 7382]);
```

## Pitfalls

> **The id in the path is not the service's**
> 
> These endpoints expect the **request id**; passing a service id answers `404`. Both are numbers, so the mistake never lands on the wrong record quietly, but it is easy to make: in the list `id` is the request's and `service_id` is the service's.

> **Approval cannot be undone**
> 
> Approving an already approved request answers `not_pending`, and deleting it does **not** bring the ownership back; only the record goes. The only way to reverse a handover is a new request in the other direction.

> **A hook can block the handover**
> 
> A `blocked_by_gate` error does not mean your request was wrong; it means an addon in the installation refused the handover. That is how non-transferable service types are defined, so if you wrote your own hook, look there first.

> **Do not confuse it with a licence transfer**
> 
> The handover here changes **who owns** the service: the service stays as it is and its invoices start going to another client. Moving a licence from one server to another is entirely different work living on its own endpoints.

## Related Articles

- [Service Endpoints](https://dev.wisecp.com/en/service-endpoints)
- [Licence Transfers](https://dev.wisecp.com/en/licence-transfers)
