API Authentication Guide
Authentication Guide
The ReceiptRoller API uses the OAuth 2.0 authorization code flow. A third-party developer registers an app, and once a business owner authorizes access, the developer can obtain a token and call the API.
Step 1: Register an app
- Log in to your ReceiptRoller account
- Go to Business account detail → ⋮ menu → Developer apps
- Click Create app
- Enter the app name, redirect URI, and select the API scopes you need
- After creation, a
client_idandclient_secretare issued
client_secret is shown only once. Be sure to copy it and store it securely. If you lose it, you'll need to regenerate it (which immediately invalidates the existing secret).Step 2: Redirect the business owner to the authorization screen
When a business owner links their account to your app, redirect their browser to the following URL:
GET https://your-domain/oauth/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=YOUR_REDIRECT_URI
&response_type=code
&scope=store.products.read store.orders.read
&state=RANDOM_STATE_STRING
| Parameter | Required | Description |
|---|---|---|
client_id |
Yes | The app's client ID |
redirect_uri |
Yes | Must exactly match the redirect URI registered for the app |
response_type |
Yes | Specify code |
scope |
Yes | A space-separated list of scopes (see Available scopes below) |
state |
Recommended | A random string to prevent CSRF attacks. It is returned as-is on the callback. |
code_challenge |
Optional | PKCE code challenge (the base64url-encoded SHA-256 hash of the code_verifier) |
code_challenge_method |
Optional | S256 (required when using PKCE) |
The business owner sees a consent screen showing the app name and the access permissions being requested. They can choose to allow or deny.
Step 3: Receive the authorization code
If access is granted, the business owner's browser is redirected to redirect_uri:
GET https://your-app.com/callback
?code=AUTHORIZATION_CODE
&state=RANDOM_STATE_STRING
If denied:
GET https://your-app.com/callback
?error=access_denied
&state=RANDOM_STATE_STRING
Step 4: Exchange the code for a token
From your server side (not the browser), exchange the code for an access token with the following POST request:
POST /api/v1/auth/token
Content-Type: application/json
{
"grantType": "authorization_code",
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET",
"code": "AUTHORIZATION_CODE",
"redirectUri": "YOUR_REDIRECT_URI"
}
Success response (200):
{
"accessToken": "aBcDeFgH1234567890...",
"refreshToken": "xYzAbCdEfG098765...",
"tokenType": "Bearer",
"expiresIn": 3600,
"scope": "store.products.read store.orders.read"
}
| Field | Description |
|---|---|
accessToken |
Used when calling the API. Valid for 1 hour. |
refreshToken |
Used to obtain a new token when the access token expires. Valid for 30 days. |
expiresIn |
The token's lifetime in seconds. 3600 = 1 hour |
scope |
The scopes the business owner approved |
Step 5: Call the API
Include the access token in the Authorization header of your request:
GET /api/v1/store/products
Authorization: Bearer aBcDeFgH1234567890...
Content-Type: application/json
The API returns data associated with the business account that authorized the app. You can access only the data allowed by the approved scopes.
Step 6: Refresh an expired token
When the access token expires, use the refresh token to obtain a new token pair:
POST /api/v1/auth/token
Content-Type: application/json
{
"grantType": "refresh_token",
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET",
"refreshToken": "xYzAbCdEfG098765..."
}
The response format is the same as Step 4. The old access token and refresh token are invalidated, and a new pair is returned.
Revoking a token
To revoke an access token or refresh token:
POST /api/v1/auth/revoke
Content-Type: application/json
{
"token": "the token to revoke",
"clientId": "YOUR_CLIENT_ID"
}
A business owner can also revoke an app's access at any time from their account settings.
Error responses
| HTTP status | Error | Description |
|---|---|---|
| 400 | unsupported_grant_type |
Only authorization_code and refresh_token are supported |
| 400 | invalid_request |
A required parameter is missing |
| 401 | invalid_grant |
Invalid code, expired code, credential mismatch, or redirect URI mismatch |
| 400 | invalid_client |
Unknown client_id |
| 400 | invalid_redirect_uri |
The redirect_uri doesn't match the registered URI |
Available scopes
Store
| Scope | Description |
|---|---|
store.products.read |
Read product and inventory information |
store.products.write |
Create and update products |
store.orders.read |
Read order data |
store.orders.write |
Update order status |
store.customers.read |
Read customer data |
store.coupons.read |
Read coupon data |
store.coupons.write |
Create and update coupons |
store.inventory.read |
Read warehouse and stock data |
CRM (Customer Relationship Management)
| Scope | Description |
|---|---|
crm.profiles.read |
Read CRM customer profiles |
crm.segments.read |
Read CRM segments and lists |
SNS / Analytics
| Scope | Description |
|---|---|
sns.content.read |
Read SNS post data |
sns.audience.read |
Read SNS audience data |
analytics.read |
Read analytics snapshots |
Flow diagram
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ Your app │ │ Business │ │ ReceiptRoller │
│ (developer) │ │ owner │ │ platform │
└──────┬───────┘ └──────┬───────┘ └────────┬─────────┘
│ │ │
│ 1. Redirect to /oauth/authorize │
│──────────────────────▶│──────────────────────▶│
│ │ │
│ │ 2. Show consent screen │
│ │◀──────────────────────│
│ │ │
│ │ 3. Allow │
│ │──────────────────────▶│
│ │ │
│ 4. Redirect with ?code=authorization code │
│◀──────────────────│◀──────────────────────│
│ │ │
│ 5. POST /api/v1/auth/token │
│ (code + client_id + client_secret) │
│────────────────────────────────────────────▶│
│ │
│ 6. { accessToken, refreshToken } │
│◀────────────────────────────────────────────│
│ │
│ 7. GET /api/v1/store/products │
│ Authorization: Bearer {accessToken} │
│────────────────────────────────────────────▶│
│ │
│ 8. { data: [...] } │
│◀────────────────────────────────────────────│
Security best practices
- Keep the
client_secreton the server side only. Never include it in frontend code or a mobile app. - Always verify the
stateparameter to prevent CSRF attacks. - Use PKCE (
code_challenge/code_verifier) for mobile apps and single-page app scenarios. - Store tokens securely. Don't log access tokens or include them in URLs.
- Implement token refresh before expiry to avoid service interruptions.
- Request only the scopes your app actually needs.