Authentication Methods

Loop provides two authentication methods for accessing our APIs:

API Keys
Most Loop API endpoints use API key authentication. API keys are simple to implement and work for most integration scenarios. You create API keys in the Loop Admin and include them in the X-Authorization header of your requests.

OAuth 2.0
OAuth 2.0 is currently only required for accessing the Label API and Webhooks API. For all other API endpoints, you can use API keys.

If you’re unsure which authentication method you need, check the documentation for the specific endpoints you want to use.


API Key Authentication

All requests to most Loop API endpoints require an API key with the correct scopes.

Create API Keys

  1. Log in to the Loop Admin.
  2. Navigate to Returns Management > Tools & integrations > Developer Tools.
  3. Click Generate API key, then add the scopes you want to allow the API key to access.
ScopeValueAvailable Endpoints
ReturnsreturnsProcess Return, Remove Line Items, Cancel Return, Flag Return, Close Return, Get Return Notes, Create Return Note, Detailed Returns List, Get Return Details, Advanced Shipping Notice, Grade Items, Assess Dispositions, Create Fraud Report
OrdersordersCreate Return Deep Link, Create Return Deep Link with QR Code, List Blocklist Items, Get Blocklist Item, Create Blocklist Item, Delete Blocklist Item, List Allowlist Items, Create Allowlist Item, Get Allowlist Item, Delete Allowlist Item
CartscartsCreate Cart, Get Cart, Update Cart, Delete Cart
Developer Toolsdeveloper_toolsGet Webhooks, Create Webhook, Delete Webhook, Update Webhook
Destinations (Read)destinations:readGet All Destinations, Get Destination Details
Destinations (Write)destinations:writeCreate Destination, Update Destination, Delete Destination
Happy Returns Shipments (Read)happy_returns_shipments:readGet Shipment Information, Get Shipments, Get Shipment Items
You can also refer to the documentation for each endpoint to determine the required scopes.
  1. Give the API key a descriptive name, then click Generate.

Edit keys

In addition to creating API keys, you can also edit and delete keys from the Developer Tools page.

Using API Keys

Every request requires a key to be provided in the X-Authorization header.

An invalid API key will result in a 401 Unauthorized response code. An API key is invalid if or lacks the required scopes for the requested endpoint.

{
  "error": {
    "code": "401",
    "http_code": "GEN-UNAUTHORIZED",
    "message": "Unauthorized."
  }
}

OAuth 2.0 Authentication

OAuth 2.0 authorization is currently only required for accessing the Label API and Webhooks API.

This guide walks you through obtaining an authorization code, exchanging it for an access token, and some implementation best practices.

Base URLs

ServiceURL
OAuth Serverhttps://oauth.loopreturns.com
API Serverhttps://api.loopreturns.com

Authorization Code Flow

Step 1: Obtaining OAuth credentials

Fill in the required fields on this form to apply for OAuth credentials.

After reviewing your information, Loop creates a client application for you which includes a client ID and secret. Loop will then provide you with your OAuth credentials.

Installation URL

When filling out the registration form, you’ll be asked to provide an Installation URL. This is the URL where Loop will redirect users when they click “Install” on your integration within the Loop Admin.

Your Installation URL should:

  • Initiate the OAuth flow by redirecting users to Loop’s authorization endpoint (Step 2)
  • Be a secure HTTPS endpoint that you control
  • Optional: Allow users to configure settings for your integration
  • Optional: Create webhooks for your integration via the Webhooks API

For example, if your Installation URL is https://yourapp.com/install/loop, when a Loop user wants to install your integration, they’ll be redirected to this URL, and your application should then redirect them to the OAuth authorization endpoint to begin the credential exchange process.

Important: Loop will append an organization query parameter to your Installation URL (e.g., https://yourapp.com/install/loop?organization=acme-corp). You must capture this parameter and include it in your authorization request to Loop’s OAuth endpoint.

Step 2: Redirect the user for authorization

Redirect the user to Loop’s authorization endpoint so they can approve your app:

GET https://oauth.loopreturns.com/authorize

Query Parameters

ParameterRequiredDescription
response_typeMust be code
client_idYour app’s client ID
redirect_uriMust exactly match what you registered in the Google form
scopeSpace-separated scopes (e.g. read:returns write:returns).
See Authentication for more on scopes.
stateRandom string to prevent CSRF
organizationOrganization identifier passed from Loop

Example

https://oauth.loopreturns.com/authorize?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.com/callback
  &scope=read:returns write:returns
  &state=random123
  &organization=acme-corp

After the user approves access, they’ll be redirected to your redirect URI like this:

https://yourapp.com/callback?code=AUTH_CODE&state=random123

Step 3: Exchange the code for an access token

Once you receive the authorization code, exchange it for an access token using the following endpoint:

Critical: This step must be performed on your secure backend server, never in client-side code (browser, mobile app, etc.). The client_secret is highly sensitive and should never be exposed to end users.

POST https://oauth.loopreturns.com/oauth/token

Body Parameters

ParameterRequiredDescription
grant_typeMust be authorization_code
codeThe code received from the previous step
redirect_uriMust match the original redirect URI
client_idYour app’s client ID
client_secretYour app’s client secret

Example

curl -X POST https://oauth.loopreturns.com/oauth/token
  -H "Content-Type: application/x-www-form-urlencoded"
  -d "grant_type=authorization_code"
  -d "code=AUTH_CODE"
  -d "redirect_uri=https://yourapp.com/callback"
  -d "client_id=YOUR_CLIENT_ID"
  -d "client_secret=YOUR_CLIENT_SECRET"

Response

{
  "access_token": "ACCESS_TOKEN",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "REFRESH_TOKEN",
  "scope": "read:returns write:returns"
}

Token Expiration: Access tokens have a TTL (time to live) of 1 hour (3600 seconds). The expires_in field in the response indicates the number of seconds until the token expires. You’ll need to use the refresh token to obtain a new access token before it expires.

Once you’ve received the OAuth access token, use the access token to authenticate your API requests by passing it in the Authorization header.

Refreshing your access token

Access tokens expire after 1 hour. When your token expires, call the POST https://oauth.loopreturns.com/oauth/token endpoint with refresh_token in the grant_type parameter to obtain a new token.

Body Parameters

ParameterRequiredDescription
grant_typeMust be refresh_token
refresh_tokenThe token you previously received
client_idYour app’s client ID
client_secretYour app’s client secret

Example

curl -X POST https://oauth.loopreturns.com/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=REFRESH_TOKEN" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

Troubleshooting

Ensure your redirect_uri exactly matches what you registered — including trailing slashes!

  • Double-check your client_secret if you’re getting 401s from the token endpoint.
  • Always verify the state parameter to prevent CSRF attacks.

Test your integration

Once you’ve received an OAuth token, call the endpoint below to verify that your token is working:

curl https://api.loopreturns.com/api/v1/me \
  -H "Authorization: Bearer ACCESS_TOKEN"

Available OAuth Scopes

When requesting authorization, you must specify the scopes your application needs. Scopes define what resources your application can access on behalf of the user.

Scope Format

OAuth scopes follow the pattern: {resource}:{action}

  • Resources: API resource categories
  • Actions: read (view data) or write (create/modify data)

Available Scopes

ScopeDescription
labels:readRead access to labels
labels:writeCreate and modify labels
label_requests:readRead access to label requests
label_requests:writeCreate and modify label requests
developer_toolsCreate and modify webhooks

Scope Selection Best Practices

Principle of Least Privilege: Only request the minimum scopes necessary for your integration to function. This improves security and user trust.

  • Start minimal: Begin with read-only scopes and add write permissions only when needed
  • Be specific: Request scopes for specific resources rather than broad permissions

Error Handling

Understanding and properly handling OAuth errors is crucial for a robust integration. This section covers all possible error scenarios and how to handle them.

OAuth Authorization Errors

The possible error codes follow the OAuth 2.0 specification as defined in RFC 6749 Section 5.2. Common error codes include invalid_request, unauthorized_client, access_denied, unsupported_response_type, invalid_scope, server_error, and temporarily_unavailable.

Authorization Error Response Format

When an error occurs during authorization, the user is redirected to your redirect_uri with error parameters:

https://yourapp.com/callback?error=access_denied
  &error_description=The+user+denied+the+request
  &state=random123

Security & Token Management Best Practices

Proper security and token management are critical for protecting user data and maintaining trust. Follow these best practices to secure your OAuth implementation.

Security Best Practices

Client Secret Protection

Critical: Never expose your client_secret in client-side code, mobile apps, or public repositories.

✅ Do:

  • Store client secrets in environment variables or secure configuration management
  • Use the client secret only on your secure backend servers
  • Use different credentials for development, staging, and production environments

❌ Don’t:

  • Include client secrets in frontend JavaScript, mobile apps, or any client-side code
  • Commit client secrets to version control
  • Log client secrets in application logs
  • Share client secrets via email or insecure channels
import os
from django.conf import settings

# ✅ Correct: Using environment variables
CLIENT_SECRET = os.environ.get('LOOP_CLIENT_SECRET')

# ✅ Correct: Using Django settings
CLIENT_SECRET = settings.LOOP_CLIENT_SECRET

# ❌ Wrong: Hardcoded in source code
CLIENT_SECRET = "your_actual_client_secret_here"

HTTPS Requirements

All OAuth endpoints and your application endpoints must use HTTPS in production.

  • Authorization URLs: Must use HTTPS to prevent code interception
  • Redirect URIs: Must use HTTPS to protect authorization codes
  • Token exchanges: Must use HTTPS to protect client credentials
  • API requests: Must use HTTPS to protect access tokens

State Parameter Security

The state parameter prevents CSRF attacks and should be cryptographically secure:

import secrets
import hashlib

def generate_secure_state():
    # Generate a cryptographically secure random string
    return secrets.token_urlsafe(32)

def verify_state(received_state, expected_state):
    # Use constant-time comparison to prevent timing attacks
    return secrets.compare_digest(received_state, expected_state)

# Usage
state = generate_secure_state()
# Store state in session for later verification
request.session['oauth_state'] = state