Paraşüt API'ye yapılan her istek bir access_token ister ve bu token yalnızca OAuth 2.0 Authorization Code akışıyla alınır. Kullanıcı adı ve şifreyle doğrudan token almak ya da sabit bir API anahtarı kullanmak yoktur: kullanıcı Paraşüt giriş ekranında oturum açar, uygulamanıza izin verir ve siz dönen kodu token'a çevirirsiniz. Paraşüt bu akışta PKCE'yi zorunlu tutar ve yalnızca S256 yöntemini kabul eder.
Bu yazıda DevHub'daki istemci bilgilerinden başlayıp yetkilendirme adresinin parametrelerine, code_verifier ve code_challenge üretimine, token değişimine, yenileme akışına ve company_id değerinin nasıl bulunacağına kadar tüm adımları Paraşüt'ün kendi belgelerine dayanarak anlatıyoruz. Entegrasyonun genel resmini görmek isterseniz önce Paraşüt entegrasyonu genel rehberine göz atabilirsiniz; OAuth kavramlarına yabancıysanız OAuth2 ve OIDC rehberimiz temeli verir.
Başlamadan önce: DevHub istemci bilgileri

Paraşüt API'yi kullanmak için önce devhub.parasut.com üzerinden doğru başvuru seçeneğiyle başvuru yapmanız gerekir. Başvuru tamamlandığında ihtiyacınız olan değerler Geliştirici Portalı'ndaki API Bilgilerim sayfasında görünür. Akış boyunca üç değerle çalışırsınız:
| Değer | Nereden gelir | Dikkat edilecek nokta |
|---|---|---|
| CLIENT_ID | API Bilgilerim sayfası | Uygulamanızın kimliği; gizli değildir |
| REDIRECT_URI | Başvuruda verdiğiniz geri dönüş adresi | En fazla 3 adres; başvurudan sonra değiştirilemez, değişiklik için yeni başvuru gerekir |
| CLIENT_SECRET | API Bilgilerim sayfası (confidential client) | Yalnızca backend tarafında saklanır; e-posta ile gönderilmez |
Secret, başvuru tamamlama ekranında ve API Bilgilerim sayfasında maskeli olarak gösterilir; oradan görüntüleyip kopyalayabilirsiniz. Sızdığından şüphelenirseniz aynı sayfadaki Secret'ı Yenile düğmesini kullanın. Yeni secret oluşturulduğu anda eski secret hemen geçersiz olur ve geçiş süresi yoktur: eski secret ile yapılan token ve yenileme istekleri reddedilir. Bu yüzden yeniledikten hemen sonra sunucunuzdaki değeri güncelleyin. Daha önce alınmış access token'lar süreleri dolana kadar çalışmaya devam eder.
Staging ve production istemcisi
Paraşüt iki ortam sunar. Staging istemciniz, sizin için oluşturulan bir Paraşüt staging hesabına bağlıdır. Hesap henüz yoksa API Bilgilerim sayfasından oluşturabilirsiniz; Paraşüt'ün resmi örnek uygulamasının belgesinde bu adım API Bilgilerim, Staging, Staging Hesabı Oluştur olarak geçer ve girişte bu hesabın kullanıcı adı ile geçici şifresi kullanılır. Bu staging hesabı 6 ay sonra otomatik olarak silinir, dolayısıyla kalıcı test verisi beklemeyin.
| Staging | Production | |
|---|---|---|
| Kimlik sunucusu | id-staging.parasut.com | id.parasut.com |
| OIDC Discovery | id-staging.parasut.com/realms/parasut/.well-known/openid-configuration | id.parasut.com/realms/parasut/.well-known/openid-configuration |
| Redirect URI kuralı (örnek uygulamaya göre) | https ya da yalnızca localhost için http; API Bilgilerim sayfasında düzenlenebilir | Gerçek alan adında https; localhost olmaz, en fazla 3 adres, sonradan değişmez |
| Hesap ömrü | 6 ay sonra otomatik silinir | Gerçek firma verisi |
Staging API adresi resmi spec'te yazmaz; Paraşüt'ün resmi örnek uygulaması varsayılan olarak https://api.heroku-staging.parasut.com/v4 kullanır. Production API adresi ise https://api.parasut.com/v4 şeklindedir.
Confidential client ve public client
Yeni API istemcileri varsayılan olarak confidential client olarak oluşturulur. Bu türde token alma ve yenileme istekleri backend'inizden, PKCE'ye ek olarak client_secret ile yapılır. Backend'i olmayan ve secret'ı güvenli biçimde saklayamayan mobil ya da masaüstü uygulamalar başvuru sırasında public client talep edebilir. Public client'ın secret'ı yoktur; token alma ve yenileme son kullanıcının cihazında yalnızca PKCE ile yapılır ve isteklerde client_secret gönderilmez. Daha önce oluşturulmuş istemciler public olarak kalır. İstemcinizin türünü API Bilgilerim sayfasında görebilirsiniz.
Akışa genel bakış
- Uygulamanız rastgele bir
stateve bircode_verifierüretir, kullanıcıyı yetkilendirme adresine yönlendirir. - Kullanıcı Paraşüt giriş ekranında e-posta ve şifresiyle oturum açar.
- Consent adımında uygulamanızın hangi verilere erişeceği gösterilir ve kullanıcı onaylar.
- Tarayıcı
REDIRECT_URIadresinizecodevestateparametreleriyle döner. - Backend kodu,
code_verifierile birlikte token ucuna gönderipaccess_tokenverefresh_tokenalır. - API isteklerinde
Authorization: Bearerbaşlığıyla token gönderilir.
Akışı üç şekilde başlatabilirsiniz: uygulamanıza koyduğunuz bir "Paraşüt Hesabımı Bağla" düğmesiyle, uygulamanız Paraşüt Uygulamalar sayfasında yayınlanıyorsa oradaki "Kullanmaya Başla" seçeneğinin sizin /oauth/start benzeri ucunuza yaptığı deep-link ile ya da arayüzü olmayan senaryolarda doğrudan backend yönlendirmesiyle. Paraşüt, PKCE ve state değerinin güvenle yönetilebilmesi için yetkilendirme adresinin her zaman sizin uygulamanızda üretilmesini ister; IdP tarafından başlatılan akışlar önerilmez.
Adım 1: Yetkilendirme adresi ve parametreleri

Kullanıcıyı şu adrese yönlendirirsiniz (production örneği):
https://id.parasut.com/realms/parasut/protocol/openid-connect/auth
?client_id=CLIENT_ID
&redirect_uri=REDIRECT_URI
&response_type=code
&scope=openid
&state=RANDOM_STATE
&code_challenge=BASE64URL_SHA256_CODE_VERIFIER
&code_challenge_method=S256
&kc_idp_hint=parasut| Parametre | Değer | Açıklama |
|---|---|---|
| client_id | CLIENT_ID | Size verilen uygulama kimliği |
| redirect_uri | REDIRECT_URI | Başvuruda kayıtlı geri dönüş adresi, birebir aynı yazılmalı |
| response_type | code | Authorization Code akışı için sabit |
| scope | openid | Talep edilen izin kapsamı |
| state | Rastgele dize | CSRF koruması; oturumla eşleştirilir |
| code_challenge | Base64url(SHA256(code_verifier)) | Zorunlu PKCE özeti |
| code_challenge_method | S256 | Zorunlu; yalnızca S256 desteklenir |
| kc_idp_hint | parasut | Opsiyonel; kimlik sağlayıcı seçim ekranını atlayıp doğrudan Paraşüt girişini açar |
code_challenge içermeyen ya da S256 dışında bir yöntem kullanan yetkilendirme istekleri reddedilir.
code_verifier ve code_challenge nasıl üretilir?
RFC 7636 (PKCE) kurallarına göre code_verifier; A-Z, a-z, 0-9, -, ., _, ~ karakterlerinden oluşan, en az 43 en fazla 128 karakterlik, yüksek entropili rastgele bir dizedir. code_challenge ise bu dizenin SHA-256 özetinin Base64url ile, sondaki = dolgu karakterleri olmadan kodlanmış hâlidir. Her yetkilendirme isteği için yeni bir verifier üretip oturumda saklarsınız; token isteğinde ham verifier değerini gönderirsiniz.
Aşağıdaki Node.js kodu Paraşüt'ün resmi örnek uygulamasındaki oauth.ts dosyası örnek alınarak sadeleştirildi. 32 rastgele baytın Base64url karşılığı 43 karakterdir ve izin verilen karakter kümesinin içinde kalır:
import { createHash, randomBytes } from 'node:crypto';
const AUTH_BASE = process.env.PARASUT_AUTH_BASE;
// prod: https://id.parasut.com/realms/parasut/protocol/openid-connect
// staging: https://id-staging.parasut.com/realms/parasut/protocol/openid-connect
export function authorizationUrl(session) {
const codeVerifier = randomBytes(32).toString('base64url'); // 43 karakter
const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
const state = randomBytes(16).toString('base64url');
// Sunucu tarafı oturumda sakla; tarayıcıya verifier gönderme
session.parasutLogin = { state, codeVerifier, createdAt: Date.now() };
const params = new URLSearchParams({
client_id: process.env.PARASUT_CLIENT_ID,
redirect_uri: process.env.PARASUT_REDIRECT_URI,
response_type: 'code',
scope: 'openid',
state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
kc_idp_hint: 'parasut',
});
return `${AUTH_BASE}/auth?${params}`;
}Adım 2: Callback ve token değişimi
Kullanıcı onay verdikten sonra tarayıcı REDIRECT_URI?code=...&state=... adresine döner. İlk iş state değerini oturumdakiyle karşılaştırmaktır; eşleşmiyorsa isteği reddedin. Örnek uygulama state değerini tek kullanımlık tutar ve 10 dakikadan eski bekleyen girişleri siler; aynısını yapmanızı öneririz.
Ardından kodu token ucuna gönderirsiniz. Confidential client'larda bu istek mutlaka sunucu tarafında yapılır:
curl -X POST https://id.parasut.com/realms/parasut/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=$PARASUT_CLIENT_ID" \
-d "client_secret=$PARASUT_CLIENT_SECRET" \
-d "code=<AUTHORIZATION_CODE>" \
-d "redirect_uri=$PARASUT_REDIRECT_URI" \
-d "code_verifier=<CODE_VERIFIER>" client_id ve client_secret değerlerini form alanı yerine HTTP Basic ile de gönderebilirsiniz: Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET). Bu durumda client_secret satırını gövdeden çıkarın. Public client kullanıyorsanız client_secret hiç gönderilmez.
Başarılı yanıt şu biçimdedir:
{
"access_token": "<ACCESS_TOKEN>",
"token_type": "Bearer",
"expires_in": 7200,
"refresh_token": "<REFRESH_TOKEN>",
"refresh_expires_in": 2592000,
"scope": "openid"
}Aynı adımın Node.js karşılığı:
export async function handleCallback(query, session) {
const login = session.parasutLogin;
delete session.parasutLogin; // tek kullanımlık
if (!login || query.get('state') !== login.state) {
throw new Error('state eşleşmedi (CSRF koruması)');
}
if (query.get('error')) throw new Error(`Yetkilendirme reddedildi: ${query.get('error')}`);
const form = new URLSearchParams({
grant_type: 'authorization_code',
client_id: process.env.PARASUT_CLIENT_ID,
code: query.get('code'),
redirect_uri: process.env.PARASUT_REDIRECT_URI,
code_verifier: login.codeVerifier,
});
// Public client'ta secret yoktur
if (process.env.PARASUT_CLIENT_SECRET) form.set('client_secret', process.env.PARASUT_CLIENT_SECRET);
const res = await fetch(`${AUTH_BASE}/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
body: form,
});
const body = await res.json();
if (!res.ok) throw new Error(`Token isteği başarısız: ${body.error} ${body.error_description ?? ''}`);
return saveTokens(body); // kullanıcı başına, şifreli saklayın
}Token süreleri ve yenileme akışı
Yanıttaki expires_in ve refresh_expires_in değerleri saniye cinsindendir. Belgedeki örnek yanıtta access token için 7200 saniye (2 saat), refresh token için 2592000 saniye (30 gün) görünür. Sabit değer varsaymak yerine her yanıtta gelen değeri okuyup bitiş zamanını hesaplayın. Paraşüt, süre dolmadan bir miktar önce yenileme yapmanızı önerir; örnek uygulama access token'ı bitişinden 60 saniye önce yeniler.
curl -X POST https://id.parasut.com/realms/parasut/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "client_id=$PARASUT_CLIENT_ID" \
-d "client_secret=$PARASUT_CLIENT_SECRET" \
-d "refresh_token=<REFRESH_TOKEN>" Yenileme yanıtı token değişimiyle aynı biçimdedir ve genellikle yeni bir refresh_token içerir. Kural basit: bir sonraki yenilemede dönen son refresh_token değerini kullanın; yanıtta yeni bir refresh token yoksa elinizdekini saklamaya devam edin. Yenileme 400 ile reddedilirse (örnek uygulama bunu refresh token'ın artık geçersiz olduğu durum olarak ele alır) tokenları silip kullanıcıdan hesabı yeniden bağlamasını isteyin.
Neden yenilemeyi kilitlemelisiniz?
Paraşüt'ün resmi kuralları açık: yenilemeyi tek noktada, kilit ya da kuyrukla yapın ve aynı refresh_token ile paralel iki yenileme başlatmayın. Aynı anda gelen iki istek, ikisi de token'ın süresinin dolduğunu görüp yenilemeye kalkarsa biri eski refresh token'ı kullanmış olur. Tek süreçte çalışan bir Node.js servisinde paylaşılan bir Promise yeterlidir:
let refreshing = null;
export async function getAccessToken(store) {
const t = await store.load();
if (t.expiresAt - 60_000 > Date.now()) return t.accessToken;
// Aynı anda gelen tüm çağrılar tek yenilemeyi bekler
refreshing ??= refreshTokens(store).finally(() => { refreshing = null; });
await refreshing;
return (await store.load()).accessToken;
}
async function refreshTokens(store) {
const t = await store.load();
const form = new URLSearchParams({
grant_type: 'refresh_token',
client_id: process.env.PARASUT_CLIENT_ID,
refresh_token: t.refreshToken,
});
if (process.env.PARASUT_CLIENT_SECRET) form.set('client_secret', process.env.PARASUT_CLIENT_SECRET);
const res = await fetch(`${AUTH_BASE}/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
body: form,
});
if (res.status === 400) { await store.clear(); throw new Error('Yeniden bağlanma gerekli'); }
if (!res.ok) throw new Error(`Yenileme başarısız: HTTP ${res.status}`);
const b = await res.json();
const now = Date.now();
await store.save({
accessToken: b.access_token,
refreshToken: b.refresh_token ?? t.refreshToken, // dönen son refresh token
expiresAt: now + b.expires_in * 1000,
refreshExpiresAt: now + (b.refresh_expires_in ?? 0) * 1000,
});
}Servisiniz birden fazla süreçte ya da sunucuda çalışıyorsa bellek içi Promise yetmez; kilidi paylaşılan bir katmana taşıyın. Veritabanında satır kilidi ya da Redis üzerinde kısa ömürlü bir kilit bu iş için yaygın çözümlerdir. Zamanlanmış eşitleme görevlerinin de aynı token deposunu ve kilidi kullandığından emin olun; ayrıntılar için cron job rehberine bakabilirsiniz.
İlk API isteği ve company_id bulma
Token'ı her istekte Authorization: Bearer başlığıyla gönderirsiniz. Paraşüt yollarının neredeyse tamamı https://api.parasut.com/v4/{company_id}/... biçimindedir. Tek istisna GET /me ucudur; firma öneki almaz ve token sahibinin bilgilerini döndürür (GET /me belgesi). Bu uç include parametresinde user_roles, companies ve profile değerlerini kabul eder:
curl "https://api.parasut.com/v4/me?include=companies" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Accept: application/json" Yanıtta data.type değeri users olur; attributes içinde name, email ve is_confirmed bulunur. Kullanıcının erişebildiği firmalar relationships.companies.data dizisinde ve included içinde companies tipiyle gelir. Paraşüt'ün kanonik akışı şöyledir: firmaları al, kullanıcı birden fazla firmaya erişiyorsa seçtir, seçilen firmanın kimliğini company_id olarak sakla ve sonraki tüm yollarda kullan.
Token, kullanıcının o firmadaki rolüyle sınırlıdır. Kullanıcının entegrasyon kurma izni yoksa ya da rolü işlemi kapsamıyorsa 403 alırsınız ve bu isteği otomatik tekrarlamamalısınız. Olağandışı sayıda 401 veya 403 alan istemciler engellenebilir. Bu kodların tamamını hata kodları ve rate limit yazımızda ayrıntılı ele aldık.
Logout, userinfo ve OIDC Discovery

Paraşüt kimlik sunucusu OpenID Connect destekler. Production için belgelenen uçlar şunlardır:
| Uç | Adres (prod) |
|---|---|
| Authorization | https://id.parasut.com/realms/parasut/protocol/openid-connect/auth |
| Token | https://id.parasut.com/realms/parasut/protocol/openid-connect/token |
| Logout | https://id.parasut.com/realms/parasut/protocol/openid-connect/logout |
| Userinfo | https://id.parasut.com/realms/parasut/protocol/openid-connect/userinfo |
| Discovery | https://id.parasut.com/realms/parasut/.well-known/openid-configuration |
Adresleri koda sabit yazmak yerine Paraşüt OIDC Discovery belgesi üzerinden okuyabilirsiniz; bu belge tüm uç adreslerini, desteklenen scope'ları ve imzalama anahtarlarını verir. Staging'de aynı yolu id-staging.parasut.com alan adıyla kullanın. Logout ve userinfo uçlarının parametreleri Paraşüt rehberinde ayrıca açıklanmadığından, ayrıntıları discovery belgesinden ve standart OIDC davranışından kontrol etmenizi öneririz.
Sık yapılan hatalar
- Redirect URI'yi başvurudakinden farklı yazmak: tek karakter farkı bile akışı bozar. Production adresleri sonradan değiştirilemez.
code_verifierdeğerini tarayıcıda üretip saklamak. Confidential client'ta verifier ve secret yalnızca backend'de kalmalı.- Her istekte token yenilemek ya da paralel yenileme başlatmak. Süreyi izleyin ve yenilemeyi kilitleyin.
- Yenilemede eski refresh token'ı kullanmaya devam etmek. Her zaman dönen son değeri saklayın.
- Secret yenilendikten sonra backend'i güncellemeyi unutmak. Eski secret anında geçersiz olur.
Sık Sorulan Sorular
Paraşüt API için PKCE zorunlu mu?
Evet. Tüm istemcilerde PKCE zorunludur ve yalnızca S256 desteklenir. code_challenge olmadan gelen yetkilendirme istekleri reddedilir. Confidential client'ta PKCE'ye ek olarak client_secret da gönderilir.
Paraşüt access token ne kadar geçerli?
Belgedeki örnek yanıtta expires_in 7200 saniye, refresh_expires_in 2592000 saniyedir. Kodunuz sabit süre varsaymamalı, her token yanıtındaki değeri okuyup bitiş zamanını buna göre hesaplamalıdır.
Kullanıcı adı ve şifreyle token alabilir miyim?
Paraşüt'ün belgelediği tek akış Authorization Code + PKCE'dir. Kullanıcı Paraşüt giriş ekranında oturum açar ve consent adımını onaylar; sizin uygulamanız şifreyi hiç görmez.
Staging hesabım neden kayboldu?
Staging client'ınıza bağlı Paraşüt staging hesabı 6 ay sonra otomatik silinir. Gerekirse API Bilgilerim sayfasından yeni bir staging hesabı oluşturabilirsiniz.
Token aldıktan sonra ilk ne yapmalıyım?
GET /me?include=companies ile kullanıcının firmalarını alın ve company_id değerini saklayın. Ardından cari oluşturma, fatura kesme ve listeleme, filtreleme ve sayfalama adımlarına geçebilirsiniz.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Paraşüt entegrasyonu genel rehberi · Paraşüt API hata kodları ve rate limit · Paraşüt API filtreleme, sayfalama ve include · Paraşüt webhook kurulumu · OAuth2 ve OIDC rehberi · REST API güvenlik rehberi