Guides
iOS integration (Swift)
Protect iOS signup and login through your backend and the AbuseGraph session and check APIs.
Keep the secret on your backend
Never include an sk_ key in an App Store binary. Have the app call an endpoint you own with the account and device fields. Your backend creates a short-lived session, binds it to a check, and returns the verdict.
App request
import Foundation
enum SignupError: Error {
case riskCheckRejected
}
struct RiskCheckRequest: Encodable {
let email: String
let event: String
let visitorId: String?
}
struct SignupResponse: Decodable {
let ok: Bool
}
let url = URL(string: "https://your-api.example.com/signup-risk-check")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(
RiskCheckRequest(email: email, event: "signup", visitorId: visitorId)
)
let (data, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
throw SignupError.riskCheckRejected
}
let result = try JSONDecoder().decode(SignupResponse.self, from: data)
guard result.ok else { throw SignupError.riskCheckRejected }
// Continue only after the backend has approved and completed signup.Backend flow
Your backend uses its server-held secret for both API calls. Store device fields in the session, then pass the returned sessionId with the account fields to check.
const headers = {
"content-type": "application/json",
"x-api-key": process.env.ABUSEGRAPH_SECRET_KEY!,
"x-abusegraph-site": process.env.ABUSEGRAPH_SITE!,
};
const site = process.env.ABUSEGRAPH_SITE!;
const sessionResponse = await fetch("https://abusegraph.com/api/v1/session", {
method: "POST",
headers,
body: JSON.stringify({ visitorId, fingerprint }),
});
if (!sessionResponse.ok) throw new Error("Unable to create risk session");
const { sessionId } = await sessionResponse.json();
const checkResponse = await fetch("https://abusegraph.com/api/v1/check", {
method: "POST",
headers,
body: JSON.stringify({ email, event, site, sessionId }),
});
if (!checkResponse.ok) throw new Error("Unable to check account risk");
const result = await checkResponse.json();
if (result.verdict === "block") return Response.json({ ok: false }, { status: 403 });
if (result.verdict === "challenge") return Response.json({ ok: false }, { status: 428 });
// Create the account / issue the session here, after the decision.
return Response.json({ ok: true });The session expires after 24 hours. Events: signup, login, password_reset, and email_change. See Android and React Native.