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

# İlk port-in'iniz: doğru yapma rehberi (LOA, PIN ve FOC takibi)

> Numara yaşam döngüsünün tamamı için ilk kez kontrol listesi: içe aktarma öncesi belgeler ve hesap PIN'i, talepten imzalı LOA'ya sihirbaz adımları, geçiş anında FOC tarihi takibi, abone olunacak webhook'lar ve ilk port'ları durduran beş ret nedeni — her birinin çözümüyle birlikte.

# İlk port-in rehberiniz

İki operatör runbook'u — [Numarayı uçtan uca port edin](https://docs.orbit.devotel.io/guides/port-numbers.md) ve [Güvenli şekilde port-out yapın](https://docs.orbit.devotel.io/guides/port-out-numbers.md) — her uç noktayı ve kenar durumu kapsar. Bu sayfa, bir numarayı ilk kez taşıdığınızda kullanacağınız daha kısa kontrol listesidir: kararları sıraya koyar, her adımın koruduğu tek şeyi adlandırır ve duran port'ları neden olan ret sebebiyle eşleştirir. Bunu bir kez okuyun, ardından port işlemini runbook sayfaları açıkken çalıştırın.

Okumalar (`check`, `get`, `timeline`) `numbers:read` scope'unu; yazmalar (oluşturma, LOA yükleme/imzalama/gönderme, iptal, PIN ayarlama) `numbers:write` ile `owner`, `admin` veya `developer` rolünü gerektirir.

Bu sayfa iki runbook'a yeni bir şey eklemez — onların yerine geçmez, aralarındaki haritadır. İkisi de yetkili kaynaktır.

## 1. Gezinilen durum makinesi

Port işleminin iki yönü de tek yönlü bir durum makinesidir. Hangi durumda olduğunuzu bilmek, hangi eylemlerin hâlâ açık olduğunu gösterir — ilk port hatalarının çoğu bir eylemin bir durum geç yapıldığı için oluşur.

### Port-in (numara Orbit'e taşınıyor)

`submitted → reviewing → approved → completed`; `rejected` ve `cancelled` terminal dallarıdır, `supplement_submitted` ise taşıyıcı düzeltmenizi değerlendirirken bekleme durumudur:

* **submitted** — sipariş var. İptal edilebilir (`DELETE /numbers/porting/:id`). Create yanıtındaki `staging: "draft"` işareti satırın saklandığını ancak taşıyıcıya HİÇBİR ŞEY gönderilmediğini belirtir — taslak, LOA yüklenip imzalanana kadar ilerlemez.
* **reviewing** — kaybeden taşıyıcı siparişinizi ve imzalı LOA'yı tutar, CSR verilerini kontrol eder. Hâlâ iptal edilebilir. Bundan sonrası değildir.
* **approved** — kaybeden taşıyıcı kabul etmiş ve genellikle FOC tarihi vermiştir. İptal artık `409` döndürür; port tamamlanır.
* **completed** — numara Orbit hesabınızda tam bir kez canlıdır ve `number.ported` yayımlanır.
* **rejected** — taşıyıcı reddetmiştir; `rejectionReason` çevrilmiş kodu taşır. Düzeltilebilir retler 7 günlük bir `supplement` penceresi açar; gönderim satırı `supplement_submitted` durumunda bekler, taşıyıcı kabul ederse `reviewing`e döner veya tekrar reddeder.
* **cancelled** — port-in ve port-out için terminal daldır; bundan sonra hiçbir şey uygulanmaz.

LOA'nın üzerinde kendi üç durumlu makinesi vardır: `loaSignatureStatus: draft → signed → submitted` ve yalnızca ileri gider. Eski veya yanlış belgeye sahip LOA, `signed` sonrasında değiştirilemez — port'u iptal edip yeniden gönderin.

### Port-out (numara Orbit'ten taşınıyor)

Aynı ilerleme numaranın kendi durumuyla önce kapılanır: `released`, `suspended`, `parked` veya `port_out_pending` durumlarının tümü gönderimi `409` ile reddeder; bu yüzden uçuş öncesi durum kontrolü ilk çalıştırılacak şeydir. FOC geçişinden sonra kazanan taşıyıcı numaranın sahibidir — iptal yok, geri alma yok.

## 2. İçe aktarma öncesi kontrol listesi — ilk API çağrısından önce

Bölüm 5'teki her ret sınıfı bu listedeki bir şeye dayanır. Gönderdikten sonra değil, göndermeden önce çalıştırın:

1. **Kaybeden taşıyıcıdan Customer Service Record (CSR)** — tüzel kişi adı, hesap numarası, faturalama telefon numarası (BTN), hizmet adresi ve varsa transfer PIN'i; kelimesi kelimesine kopyalayın. Hafızadan yeniden yazmayın; `NAME_MISMATCH` ve `ADDRESS_MISMATCH` retleri neredeyse tamamen tahmin edilen CSR alanlarından çıkar.
2. **Yetkili imzacı** — numarayı taşıma yetkisi olan, unvanı bulunan bir kişi ve LOA'yı imzalayacak kişi. Buradaki ad, LOA imzası ve kaybeden taşıyıcının kayıtlarıyla tam eşleşmelidir.
3. **LOA taraması** — ≥200 dpi gerçek bir PDF, 30 gün içinde tarihli imza ve üzerinde listelenen tüm port numaraları. `LOA_INVALID` retleri burada yoğunlaşır.
4. **Orbit numaralarınızda port-out PIN'i zaten ayarlı** — yenilerini getirirken elinizdekileri koruyun. 4-32 karakterlik PIN'i `POST /numbers/:id/port-out-protection` ile ayarlamak, tek başına bir kimlik bilgisinin port-out gönderemeyeceği anlamına gelir; eşleşen PIN olmadan taşıyıcıya ulaşmadan önce `401` reddi gelir. PIN'i ayarladığınız gün secret store'a kaydedin — kurtarma yolu yoktur, yalnızca rotasyon vardır.
5. **Ülke kurallarını bilin** — ABD/Kanada numaraları canlı eşzamanlı taşınabilirlik kontrolü alır; başka yerler `check-not-supported` döndürür ve uygunluk manueldir. Toll-free numaralar LNP akışını hiç kullanmaz; `POST /numbers/porting/toll-free/resporg` ile RespOrg değişikliğiyle taşınır.
6. **Uygunluk kararı elinizde** — `POST /numbers/porting/pre-validate` çalıştırın ve `ready: true` ile kapılayın. Bu çağrı taşınabilirlik kontrolü ile CSR alanlarını birlikte linter'dan geçirir.

Kontrol listesindeki herhangi bir satır çözülmediyse durun. İlk create'den önce kapı disiplini, ilk port'un üç hafta içinde reddedilen bir porta dönüşmesini engeller.

## 3. Adım adım — sihirbaz sırası

Panodaki **Number Porting** formu tam bu sırayı izler; API karşılığı runbook'tadır. Önemli olan sıranın kendisidir:

1. **Numaraları girin** (satır başına bir E.164) → preflight kapısını çalıştırın. Uygun değilse ilerlemeyin.
2. **Taşıyıcı + CSR alanlarını doldurun** — kaybeden taşıyıcı, hesap numarası, PIN (ABD kablosuz), yetkili imzacı, hizmet adresi — CSR'nin tam kopyaları.
3. **Taslak olarak kontrol noktası oluşturun** — `draft: true`, isteği `staging: "draft"` ile yerel olarak saklar ve taşıyıcıya hiçbir şey dokundurmaz. Aynı numara setiyle yeniden oluşturma idempotent'tir; tekrar ziyaret veya yenileme kontrol noktasını çatallamaz.
4. **LOA'yı platforma yükleyin** (`POST /numbers/porting/:id/loa`) — 10 MB altında PDF/görüntü.
5. **İmzalayın** (`.../loa/sign`) — imzacı adı, e-posta, onay. İkinci imza `422` döndürür; buraya ikinci kez geldiyseniz başka bir sorun vardır.
6. **Gönderin** (`.../loa/submit`) — `signed` durumunda geçerli tek eylem. Kaybeden taşıyıcının 7-14 iş günlük incelemesi burada başlar.
7. **FOC'u takip edin** — `GET /numbers/porting/:id/timeline`, düz durumu zaman damgalı aşama girdilerine genişletir ve taşıyıcı bir tarih verdiği anda `focDate`'i ekler. `status: completed` olana kadar izleyin.
8. **Geçişte `number.ported` tam bir kez gelir** — numara, satın alınmış bir DID ile aynı normal yaşam döngüsüne girer (otomatik yenileme, bırakma, yeniden atama). Port edilmiş numaradaki SMS, satın alınmış numaradakiyle aynı çalışır.
9. **Taslağı park etmeyin** — taslak taşıyıcıya ulaşmaz, FOC almaz ve tamamlanmaz. Pano bu durumu tam olarak “draft — LOA pending” olarak işaretler. İmzacı yoksa veya CSR bilinmiyorsa taslağı iptal edin ve hazır olduğunuzda yeniden oluşturun; sessizce yaşlanan kontrol noktası, ilk port'un sessizce durmasının en yaygın yoludur.

Port `submitted` veya `reviewing` durumundaysa ve bir şey yanlışsa `DELETE /numbers/porting/:id` ile iptal edin. `approved` sonrasında iptal `409` döndürür — port taşıyıcının takviminde tamamlanır.

## 4. Durum webhook'ları — neye abone olmalı

Bir webhook uç noktası kaydedin ve yoklamak yerine port olaylarına abone olun. İlk port'u ilerleten olaylar:

| Olay | Ne zaman gelir | Bununla ne yapmalı |
| - | - | - |
| `number.ported` | Port-in tamamlandı ve numara hesabınızda canlı. Tam bir kez yayımlanır. | Yönlendirmeyi kesin, “canlısınız” bildirimini gönderin, port sonrası provizyonu başlatın. |
| `porting.request.loa_signed` | Platform içi LOA imzası yakalandı. | Dahili kontrol listesini ilerletin; LOA gönderme adımını kuyruğa alın. |
| `porting.request.supplement_submitted` | Taşıyıcının istediği ek belge gönderildi. | Düzeltme penceresi geri sayımını sıfırlayın; dosyanın sahibi operatörü uyarın. |
| `porting.request.manual_review_required` | Port manuel incelemeye düştü. | İnsan takibi için işaretleyin; otomatik yeniden denemeyin. |
| `porting.request.cancelled` | Siz veya yönetici talebi iptal etti. | Aktif panodan kaldırın; nedeni kaydedin. |
| `number.port_out.requested` | Port-out gönderildi ve taşıyıcı tarafından kabul edildi. | İptal penceresi geri sayımını başlatın — FOC geçişinden sonra iptal artık geçerli değildir. |
| `number.port_out.cancelled` | Port-out isteği geçişten önce iptal edildi. | Numarayı “ayrılıyor” panonuzdan kaldırın. |

Teslimat en az bir kez yapılır; bu nedenle alıcınızda olay `id`'sine göre tekilleştirin. Olaylar gerçek zamanlı güncellemeleri sürer; günlük `GET /numbers/porting` + `timeline` taraması kaçırılanları uzlaştırır. Tam katalog [Webhook Events](https://docs.orbit.devotel.io/webhooks/events.md), imza doğrulama [Webhook Security](https://docs.orbit.devotel.io/webhooks/security.md) sayfasındadır.

## 5. Yaygın retler — her biri için çözüm

`GET /numbers/porting/:id/timeline`, taşıyıcı ret kodlarını önem derecesi `operator_fixable` (kendi verinizi düzeltin), `needs_carrier` (kaybeden taşıyıcı çözmeli) veya `fatal` (numara hiç taşınamaz) olarak etiketlenmiş sade bir `summary` + `recommendedAction` biçimine çevirir. İlk port'ları durduran beş neden:

| Ret | Neden olur | Çözüm |
| - | - | - |
| `NAME_MISMATCH` | Talepteki tüzel kişi adı CSR'den farklı — çoğu kez tahmin edilen veya kısaltılmış biçim (“Acme, Inc.” ile “ACME INC” gibi). | CSR'yi alın, 7 günlük düzeltme penceresi içinde adı birebir kopyalayarak supplement gönderin. |
| `ADDRESS_MISMATCH` | Hizmet adresi farklı — daire numaraları, ZIP+4 ile ZIP, kısaltmalar. | Taşıyıcıdan alınan tam CSR adresiyle, değiştirmeden alıntılayarak supplement gönderin. |
| `BTN_MISMATCH` | Çok satırlı hesaplarda gönderilen faturalama telefon numarası taşıyıcının fatura kaydıyla uyuşmuyor. | BTN'yi CSR'den alın (hesap numarasından ayrı bir alandır) ve supplement gönderin. |
| PIN reddi (`401 PIN_MISMATCH` port-out'ta; port-in'de taşıyıcı PIN reddi) | Kaybeden taşıyıcının transfer PIN'i eksik veya yanlış. | PIN'i taşıyıcıyla doğrulayın — port onsuz ilerleyemez. Göndermeden önce hesap ve PIN'i kontrol listesine ekleyerek bunu baştan önleyin. |
| `LOA_INVALID` | LOA numaraları eksik, imzasız, ekran fotoğrafı, 30 günden eski veya okunamayacak kadar düşük çözünürlükte. | ≥200 dpi gerçek PDF oluşturun; tam numara listesi ve son 30 gün içinde tarihli imza ekleyin; port'u iptal edip düzeltilmiş belgeyle yeniden gönderin. |

`fatal` önem derecesindeki (`NUMBER_NOT_PORTABLE`) retler supplement için uygun değildir — numara kaybeden taşıyıcıda kalmalıdır. Bilinmeyen taşıyıcı kodları ham taşıyıcı metnini korur; böylece tam nedenle eskale edebilirsiniz.

## 6. API üzerinden izleyin — çalıştırılabilir dört çağrı

Yukarıdaki kontrol listesi uç noktaları adlandırır; burada en küçük çalıştırılabilir döngü vardır. Aynı dört çağrı cURL, Node.js, Python, Go, Ruby ve PHP'de, her birinin yaptığı işe göre kapsamlandırılmıştır (bu parçaların yansıttığı tam uç nokta referansı [Number Porting](https://docs.orbit.devotel.io/numbers/porting.md)). Check ve timeline okumalarında `numbers:read`, create'te `numbers:write` scope'u olan bir API anahtarı kullanın.

```bash cURL theme={null}
# Step 1 — pre-import check (numbers:read): one live carrier call.
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/porting/check \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "phoneNumber": "+14155551234", "currentCarrier": "Acme Telecom" }'
# Gate on: eligible === true

# Step 2 — create the port (numbers:write): draft: true checkpoints the row
# locally (staging: "draft") and dispatches nothing until the LOA is signed.
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/porting \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "numbers": ["+14155551234"],
    "currentCarrier": "Acme Telecom",
    "country": "US",
    "accountNumber": "998877",
    "authorizedSigner": "Jane Doe",
    "serviceAddress": {
      "line1": "1 Market St",
      "city": "San Francisco",
      "state": "CA",
      "postalCode": "94105",
      "countryCode": "US"
    },
    "draft": true
  }'
# Response: { "data": { "id": "port_draft456", "staging": "draft", ... } } — keep the id

# Step 3 — FOC timeline read (numbers:read): a pure read on the stored row,
# no carrier call; echoes focDate the moment the carrier assigns one.
curl "https://api.orbit.devotel.io/api/v1/numbers/porting/port_draft456/timeline" \
  -H "X-API-Key: dv_live_sk_..."

# Step 4 — webhook receiver: one endpoint for every porting event.
curl -X POST https://api.orbit.devotel.io/api/v1/webhooks \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/orbit",
    "events": [
      "number.ported",
      "porting.request.loa_signed",
      "porting.request.supplement_submitted",
      "porting.request.manual_review_required",
      "porting.request.cancelled"
    ]
  }'
```

```typescript Node.js theme={null}
import { Devotel } from '@devotel-orbit/node';
const orbit = new Devotel({ apiKey: process.env.ORBIT_API_KEY! });

// Step 1 — pre-import check: gate on eligible before any create
const check = await orbit.request("POST", "/numbers/porting/check", {
  phoneNumber: "+14155551234",
  currentCarrier: "Acme Telecom",
});
if (check.data.eligible !== true) {
  throw new Error(`Not portable: ${check.data.reasons.join(", ")}`);
}

// Step 2 — create the port: draft checkpoint while the LOA is outstanding
const port = await orbit.request("POST", "/numbers/porting", {
  numbers: ["+14155551234"],
  currentCarrier: "Acme Telecom",
  country: "US",
  accountNumber: "998877",
  authorizedSigner: "Jane Doe",
  serviceAddress: {
    line1: "1 Market St",
    city: "San Francisco",
    state: "CA",
    postalCode: "94105",
    countryCode: "US",
  },
  draft: true,
});
const portId = port.data.id;

// Step 3 — FOC timeline read: a stored-row read; safe to poll, no carrier call
const timeline = await orbit.request("GET", `/numbers/porting/${portId}/timeline`, undefined);
// timeline.data.focDate is set the moment the carrier assigns the cutover date

// Step 4 — webhook receiver: subscribe once, then react to events instead of polling
await orbit.request("POST", "/webhooks", {
  url: "https://yourapp.com/webhooks/orbit",
  events: [
    "number.ported",
    "porting.request.loa_signed",
    "porting.request.supplement_submitted",
    "porting.request.manual_review_required",
    "porting.request.cancelled",
  ],
});
```

```python Python theme={null}
from orbit_sdk import OrbitClient
client = OrbitClient.from_env()

# Step 1 — pre-import check: gate on eligible before any create
check = client.request("POST", "/numbers/porting/check", json_body={
    "phoneNumber": "+14155551234",
    "currentCarrier": "Acme Telecom",
})
if check["data"]["eligible"] is not True:
    raise SystemExit(f"Not portable: {', '.join(check['data']['reasons'])}")

# Step 2 — create the port: draft checkpoint while the LOA is outstanding
port = client.request("POST", "/numbers/porting", json_body={
    "numbers": ["+14155551234"],
    "currentCarrier": "Acme Telecom",
    "country": "US",
    "accountNumber": "998877",
    "authorizedSigner": "Jane Doe",
    "serviceAddress": {
        "line1": "1 Market St",
        "city": "San Francisco",
        "state": "CA",
        "postalCode": "94105",
        "countryCode": "US",
    },
    "draft": True,
})
port_id = port["data"]["id"]

# Step 3 — FOC timeline read: a stored-row read; safe to poll, no carrier call
timeline = client.request("GET", f"/numbers/porting/{port_id}/timeline")
# timeline["data"]["focDate"] is set the moment the carrier assigns the cutover date

# Step 4 — webhook receiver: subscribe once, then react to events instead of polling
client.request("POST", "/webhooks", json_body={
    "url": "https://yourapp.com/webhooks/orbit",
    "events": [
        "number.ported",
        "porting.request.loa_signed",
        "porting.request.supplement_submitted",
        "porting.request.manual_review_required",
        "porting.request.cancelled",
    ],
})
```

```go Go theme={null}
package main

import (
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
)

// The four-call loop in Go stdlib. Each step is one request; print the body
// and keep data.id from step 2 for the later LOA calls.

func postJSON(key, path, body string) string {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1"+path, strings.NewReader(body))
	req.Header.Set("X-API-Key", key)
	req.Header.Set("Content-Type", "application/json")
	return do(req)
}

func get(key, path string) string {
	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1"+path, nil)
	req.Header.Set("X-API-Key", key)
	return do(req)
}

func do(req *http.Request) string {
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	out, _ := io.ReadAll(resp.Body)
	return string(out)
}

func main() {
	key := os.Getenv("ORBIT_API_KEY")

	// Step 1 — pre-import check: gate on "eligible": true before any create.
	fmt.Println(postJSON(key, "/numbers/porting/check",
		`{"phoneNumber": "+14155551234", "currentCarrier": "Acme Telecom"}`))

	// Step 2 — create the port as a draft; read "id" from the printed body.
	fmt.Println(postJSON(key, "/numbers/porting", `{
	  "numbers": ["+14155551234"], "currentCarrier": "Acme Telecom", "country": "US",
	  "accountNumber": "998877", "authorizedSigner": "Jane Doe",
	  "serviceAddress": {"line1": "1 Market St", "city": "San Francisco", "state": "CA",
	    "postalCode": "94105", "countryCode": "US"},
	  "draft": true
	}`))

	// Step 3 — FOC timeline read; safe to poll, no carrier call.
	fmt.Println(get(key, "/numbers/porting/port_draft456/timeline"))

	// Step 4 — webhook receiver: subscribe once, react to events instead of polling.
	fmt.Println(postJSON(key, "/webhooks", `{
	  "url": "https://yourapp.com/webhooks/orbit",
	  "events": ["number.ported", "porting.request.loa_signed",
	    "porting.request.supplement_submitted", "porting.request.manual_review_required",
	    "porting.request.cancelled"]
	}`))

	// Multipart upload (the bulk-csv and LOA steps below) with the stdlib:
	var buf bytes.Buffer
	w := multipart.NewWriter(&buf)
	f, _ := os.Open("port-in-batch.csv")
	part, _ := w.CreateFormFile("file", "port-in-batch.csv")
	io.Copy(part, f)
	w.Close()
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/numbers/port-in/bulk-csv", &buf)
	req.Header.Set("X-API-Key", key)
	req.Header.Set("Content-Type", w.FormDataContentType())
	fmt.Println(do(req))
}
```

```ruby Ruby theme={null}
require "net/http"
require "json"
require "uri"

KEY = ENV.fetch("ORBIT_API_KEY")
BASE = URI("https://api.orbit.devotel.io/api/v1")

def call(method, path, body = nil, form = nil)
  uri = BASE.dup
  uri.path += path
  req = method == :get ? Net::HTTP::Get.new(uri) : Net::HTTP::Post.new(uri)
  req["X-API-Key"] = KEY
  if form
    req.set_form([["file", File.open(form), filename: File.basename(form)]], "multipart/form-data")
  else
    req["Content-Type"] = "application/json"
    req.body = body && JSON.dump(body)
  end
  Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }.body
end

# Step 1 — pre-import check: gate on eligible before any create
check = JSON.parse(call(:post, "/numbers/porting/check",
  phoneNumber: "+14155551234", currentCarrier: "Acme Telecom"))
abort("Not portable: #{check["data"]["reasons"].join(", ")}") unless check["data"]["eligible"]

# Step 2 — create the port: draft checkpoint while the LOA is outstanding
port = JSON.parse(call(:post, "/numbers/porting", {
  numbers: ["+14155551234"], currentCarrier: "Acme Telecom", country: "US",
  accountNumber: "998877", authorizedSigner: "Jane Doe",
  serviceAddress: { line1: "1 Market St", city: "San Francisco", state: "CA",
                    postalCode: "94105", countryCode: "US" },
  draft: true,
}))
port_id = port["data"]["id"]

# Step 3 — FOC timeline read: a stored-row read; safe to poll, no carrier call
puts call(:get, "/numbers/porting/#{port_id}/timeline")

# Step 4 — webhook receiver: subscribe once, react to events instead of polling
puts call(:post, "/webhooks", {
  url: "https://yourapp.com/webhooks/orbit",
  events: %w[
    number.ported porting.request.loa_signed porting.request.supplement_submitted
    porting.request.manual_review_required porting.request.cancelled
  ],
})

# Multipart upload (bulk-csv / LOA steps below) with the stdlib:
puts call(:post, "/numbers/port-in/bulk-csv", nil, "port-in-batch.csv")
```

```php PHP theme={null}
<?php
$key = getenv("ORBIT_API_KEY");
$base = "https://api.orbit.devotel.io/api/v1";

function call(string $method, string $path, ?array $body = null, ?string $fileField = null): string {
    global $key, $base;
    $ch = curl_init($base . $path);
    $headers = ["X-API-Key: $key"];
    $opts = [CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => $method];
    if ($fileField !== null) {                      // multipart: bulk-csv / LOA uploads
        $opts[CURLOPT_POSTFIELDS] = ["file" => new CURLFile($fileField)];
    } elseif ($body !== null) {
        $headers[] = "Content-Type: application/json";
        $opts[CURLOPT_POSTFIELDS] = json_encode($body);
    }
    $opts[CURLOPT_HTTPHEADER] = $headers;
    curl_setopt_array($ch, $opts);
    $out = curl_exec($ch);
    curl_close($ch);
    return $out;
}

// Step 1 — pre-import check: gate on eligible before any create
$check = json_decode(call("POST", "/numbers/porting/check", [
    "phoneNumber" => "+14155551234", "currentCarrier" => "Acme Telecom",
]), true);
if (($check["data"]["eligible"] ?? false) !== true) {
    exit("Not portable: " . implode(", ", $check["data"]["reasons"]));
}

// Step 2 — create the port: draft checkpoint while the LOA is outstanding
$port = json_decode(call("POST", "/numbers/porting", [
    "numbers" => ["+14155551234"], "currentCarrier" => "Acme Telecom", "country" => "US",
    "accountNumber" => "998877", "authorizedSigner" => "Jane Doe",
    "serviceAddress" => ["line1" => "1 Market St", "city" => "San Francisco", "state" => "CA",
                         "postalCode" => "94105", "countryCode" => "US"],
    "draft" => true,
]), true);
$portId = $port["data"]["id"];

// Step 3 — FOC timeline read: a stored-row read; safe to poll, no carrier call
echo call("GET", "/numbers/porting/$portId/timeline");

// Step 4 — webhook receiver: subscribe once, react to events instead of polling
echo call("POST", "/webhooks", [
    "url" => "https://yourapp.com/webhooks/orbit",
    "events" => ["number.ported", "porting.request.loa_signed",
                 "porting.request.supplement_submitted", "porting.request.manual_review_required",
                 "porting.request.cancelled"],
]);

// Multipart upload (bulk-csv / LOA steps below) with the stdlib:
echo call("POST", "/numbers/port-in/bulk-csv", null, "port-in-batch.csv");
```

Webhook POST'u bundan sonra her durum geçişi için bir `porting.request.*` olayı gönderir — alıcınızda olay `id`'sine göre tekilleştirin ve `number.ported`'ı zaman çizelgesi durum kontrolü yerine terminal başarı sinyali olarak ele alın. Adım 2'deki taslak kontrol noktası bilerek vardır: LOA zaten imzalı ve hazırsa tek seferlik gönderim için `draft: true`'yu kaldırın. Her iki durumda da LOA sırası — yükle, imzala, gönder — dönen `portId` üzerinden ilerler ve zaman çizelgesi hareket etmeye başlar.

Check çağrısı 20 req/min ile sınırlıdır; webhook alıcısını bir kez kaydedin, döngüde yeniden kaydetmeyin. Buradan sonra bölüm 3'teki kontrol listesi devralır: LOA yükleme → imzalama → gönderme, sonra takip.

### Listenizi yükleyin — multipart toplu CSV

Bir avuçtan fazla numara mı port ediyorsunuz? Adım 2'de satır satır create yerine, tüm defteri `file` alanı altında `multipart/form-data` olarak `POST /numbers/port-in/bulk-csv`'ye verin. Listeyi şu sütunlarla `port-in-batch.csv` olarak kaydedin (sıra önemsizdir, başlık eşleştirme büyük-küçük harfe duyarsızdır):

```text theme={null}
phone_number,current_carrier_account_number,current_carrier_name,billing_name,billing_address
+14155551234,998877,Acme Telecom,Jane Doe,"1 Market St, San Francisco, CA 94105, US"
+14155555678,998877,Acme Telecom,Jane Doe,"1 Market St, San Francisco, CA 94105, US"
```

```bash cURL theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/port-in/bulk-csv \
  -H "X-API-Key: dv_live_sk_..." \
  -F "file=@port-in-batch.csv"
```

```typescript Node.js theme={null}
import { readFile } from "node:fs/promises";

const form = new FormData();
form.append(
  "file",
  new Blob([await readFile("port-in-batch.csv")], { type: "text/csv" }),
  "port-in-batch.csv",
);

const res = await fetch("https://api.orbit.devotel.io/api/v1/numbers/port-in/bulk-csv", {
  method: "POST",
  headers: { "X-API-Key": process.env.ORBIT_API_KEY! },
  body: form,
});
console.log(await res.json());
```

```python Python theme={null}
import os

import requests

with open("port-in-batch.csv", "rb") as fh:
    res = requests.post(
        "https://api.orbit.devotel.io/api/v1/numbers/port-in/bulk-csv",
        headers={"X-API-Key": os.environ["ORBIT_API_KEY"]},
        files={"file": ("port-in-batch.csv", fh, "text/csv")},
        timeout=30,
    )
print(res.json())
```

Her geçerli satır kendi port talebine dönüşür ve yanıt partinin iki tarafını bildirir — her satır için LOA sırasını sürmek üzere `request_ids`, düzeltilip yeniden yüklenmesi gereken satırlar için `validation_errors` (uçuşta olan yinelenen satırlar atlanır, böylece düzeltme yüklemesi güvenlidir):

```json 200 theme={null}
{
  "data": {
    "request_ids": ["port_a1b2c3", "port_d4e5f6"],
    "validation_errors": [
      { "row": 14, "field": "phone_number", "error": "Not a valid E.164 number" }
    ]
  }
}
```

Sınırlar: dosya başına 2 MB, istek başına 1000 satır, 30 saniyelik işleme bütçesi ve organizasyon başına dakikada 2 istek. Virgül içeren her alanı (`billing_address` neredeyse her zaman ihtiyaç duyar) tırnaklayın; zorunlu bir sütunu olmayan başlık, eksik sütunları adlandıran `422` ile tüm yüklemeyi iptal eder.

### LOA'yı dosyalayın — yükleyin, imzalayın, gönderin

LOA, adım 2'de dönen (veya her toplu satırdaki) port kimliği üzerinden üç ardışık çağrı olarak çalışır; her çağrı `loaSignatureStatus`'ı bir adım ileri taşır. ≥200 dpi gerçek bir PDF hazırlayın, imzayı 30 gün içinde tarihleyin ve her port numarasını listeleyin — `LOA_INVALID` retleri tam olarak bu üç noktada toplanır.

```bash cURL theme={null}
# 1. Upload the LOA (PDF / JPEG / PNG / HEIC, up to 10MB) under the file field
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/porting/port_draft456/loa \
  -H "X-API-Key: dv_live_sk_..." \
  -F "file=@loa.pdf"

# 2. Record the in-platform signature — moves the LOA from draft to signed
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/porting/port_draft456/loa/sign \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "signerName": "Jane Doe",
    "signerEmail": "jane@acme.com",
    "acknowledgement": "I am authorized to request this number be ported to Orbit and the information above is accurate."
  }'

# 3. Forward the signed LOA to the losing carrier — the only call valid in signed
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/porting/port_draft456/loa/submit \
  -H "X-API-Key: dv_live_sk_..."
```

```typescript Node.js theme={null}
import { readFile } from "node:fs/promises";
import { Devotel } from '@devotel-orbit/node';
const orbit = new Devotel({ apiKey: process.env.ORBIT_API_KEY! });

const portId = "port_draft456"; // from step 2 of the four-call loop

// 1. Upload — the SDK request() is JSON-only, so send the multipart via fetch
const form = new FormData();
form.append(
  "file",
  new Blob([await readFile("loa.pdf")], { type: "application/pdf" }),
  "loa.pdf",
);
await fetch(`https://api.orbit.devotel.io/api/v1/numbers/porting/${portId}/loa`, {
  method: "POST",
  headers: { "X-API-Key": process.env.ORBIT_API_KEY! },
  body: form,
});

// 2. Sign — draft → signed; signing twice returns 422
await orbit.request("POST", `/numbers/porting/${portId}/loa/sign`, {
  signerName: "Jane Doe",
  signerEmail: "jane@acme.com",
  acknowledgement:
    "I am authorized to request this number be ported to Orbit and the information above is accurate.",
});

// 3. Submit — signed → submitted; the carrier review clock starts here
await orbit.request("POST", `/numbers/porting/${portId}/loa/submit`, undefined);
```

```python Python theme={null}
import os

import requests
from orbit_sdk import OrbitClient

client = OrbitClient.from_env()
port_id = "port_draft456"  # from step 2 of the four-call loop

# 1. Upload — multipart under the file field
with open("loa.pdf", "rb") as fh:
    requests.post(
        f"https://api.orbit.devotel.io/api/v1/numbers/porting/{port_id}/loa",
        headers={"X-API-Key": os.environ["ORBIT_API_KEY"]},
        files={"file": ("loa.pdf", fh, "application/pdf")},
        timeout=30,
    )

# 2. Sign — draft → signed; signing twice returns 422
client.request("POST", f"/numbers/porting/{port_id}/loa/sign", json_body={
    "signerName": "Jane Doe",
    "signerEmail": "jane@acme.com",
    "acknowledgement": "I am authorized to request this number be ported to Orbit and the information above is accurate.",
})

# 3. Submit — signed → submitted; the carrier review clock starts here
client.request("POST", f"/numbers/porting/{port_id}/loa/submit")
```

Her çağrı, LOA bir durum ilerlemiş halde port isteğini döndürür:

```json Upload — 201 theme={null}
{ "data": { "id": "port_draft456", "loaSignatureStatus": "draft" } }
```

```json Sign — 200 theme={null}
{ "data": { "id": "port_draft456", "loaSignatureStatus": "signed" } }
```

```json Submit — 200 theme={null}
{ "data": { "id": "port_draft456", "loaSignatureStatus": "submitted" } }
```

Durum makinesi yalnızca ileri gider: LOA `signed` veya `submitted` olduğunda değiştirme yüklemesi reddedilir — port'u iptal edin ve düzeltilmiş belgeyle yeniden gönderin. `submitted` durumundan sonra adım 3'teki zaman çizelgesi devralır.

## See Also

* [Port a number end-to-end](https://docs.orbit.devotel.io/guides/port-numbers.md) — bu sayfanın haritaladığı runbook; her uç nokta sırayla işlenir.
* [Port out safely](https://docs.orbit.devotel.io/guides/port-out-numbers.md) — PIN koruması ve iptal penceresi disiplini dahil, numaraları Orbit'ten taşımanın simetrik runbook'u.
* [Number Porting endpoint sequence](https://docs.orbit.devotel.io/numbers/porting.md) — daha kısa uç nokta başvurusu.
* [Number Lifecycle](https://docs.orbit.devotel.io/numbers/lifecycle.md) — port tamamlandıktan sonra numaraya uygulananlar.
* [Webhook Events](https://docs.orbit.devotel.io/webhooks/events.md) — tam olay kataloğu.


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