Codync
무료 오픈소스 1:1 대안, Grok Bot과 Muse 대신
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기API specification and documentation for Grid API
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Grid is an API that enables modern financial institutions to easily send and receive global payments. One API to send, receive, and settle value globally—fiat, stablecoins, or Bitcoin. Always real time, always low cost, built on an open network.
See the full documentation at https://docs.lightspark.com/.
The Grid API provides endpoints for:
All API requests must include HTTP Basic Authentication using the Authorization header. The credentials should be provided in the format <api token id>:<api client secret> and then Base64 encoded.
Example:
Authorization: Basic <base64-encoded-credentials>
Where <base64-encoded-credentials> is the Base64 encoding of <api token id>:<api client secret>.
You can generate a new API token and client secret at any time in the Grid dashboard.
The API is documented using the OpenAPI 3.1 specification. The full schema is available in the openapi.yaml file in this repository.
You can view the API documentation in two formats:
mint dev to start a local documentation server for the Mintlify docsnpm run build:markdown to generate markdown documentation in generated/api-docs.mdWe provide detailed guides for common workflows with the Grid API:
For detailed implementation guides, see the Mintlify documentation which includes:
The Grid API provides endpoints across four main use cases:
POST /customers - Create a new customerPATCH /customers/{customerId} - Update a customer by IDGET /customers/{customerId} - Get a customer by IDPOST /internal-accounts - Create internal accountsPOST /external-accounts - Connect external bank accountsPOST /quotes - Create payment quotesGET /quotes/{quoteId} - Get quote detailsPOST /transactions - Execute paymentsGET /transactions/{transactionId} - Get transaction statusPOST /users - Add a new user with UMA addressPATCH /users/{userId} - Update a user by IDGET /users/{userId} - Get a user by IDGET /receiver/{receiverUmaAddress} - Get receiver information and supported currenciesPOST /quotes - Create payment quotesGET /quotes/{quoteId} - Get quote detailsPOST /transactions/{transactionId}/approve - Approve incoming paymentsPOST /transactions/{transactionId}/reject - Reject incoming paymentsGET /config - Get platform configurationPATCH /config - Update platform configurationOUTGOING_PAYMENT - Notify when a payment is sentINCOMING_PAYMENT - Notify when a payment is pending, completed, or failedThis guide outlines the process for platforms to send payments using the Grid API.
The following sequence diagram illustrates the interaction between your platform and the Grid API when sending payments:
sequenceDiagram
participant Client as Your Platform
participant Grid as Grid API
participant Bank as Banking Provider
Client->>Grid: GET /receiver/{umaAddress}
Grid-->>Client: Supported currencies and requirements
Note over Client: Select currency and amount
Client->>Grid: POST /quotes
Grid-->>Client: Quote with payment instructions
Note over Client: Execute payment using instructions
Client->>Bank: Initiate bank transfer with reference
opt Payment Status Polling
loop Until completed or failed
Client->>Grid: GET /quotes/{quoteId}
Grid-->>Client: Quote with current status
end
end
Grid->>Client: Webhook: OUTGOING_PAYMENT (status update)
Client-->>Grid: HTTP 200 OK (acknowledge webhook)
The process consists of five main steps:
First, check if an UMA address is valid and retrieve supported currencies and exchange rates.
GET /receiver/[email protected]?userId=9f84e0c2a72c4fa
Response:
{
"receivingUmaAddress": "[email protected]",
"supportedCurrencies": [
{
"currency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
},
"estimatedExchangeRate": 1.0,
"min": 100,
"max": 10000000
},
{
"currency": {
"code": "EUR",
"name": "Euro",
"symbol": "€",
"decimals": 2
},
"estimatedExchangeRate": 0.92,
"min": 100,
"max": 9000000
}
],
"requiredPayerDataFields": [
{
"name": "FULL_NAME",
"mandatory": true
},
{
"name": "BIRTH_DATE",
"mandatory": true
}
],
"lookupId": "Lookup:019542f5-b3e7-1d02-0000-000000000009"
}
Generate a quote for the payment with locked exchange rates and fees.
POST /quotes
Request body:
{
"lookupId": "Lookup:019542f5-b3e7-1d02-0000-000000000009",
"sendingCurrencyCode": "USD",
"receivingCurrencyCode": "EUR",
"lockedCurrencySide": "SENDING",
"lockedCurrencyAmount": 10000,
"description": "Invoice #1234 payment"
}
Response:
{
"quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006",
"sendingCurrency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
},
"receivingCurrency": {
"code": "EUR",
"name": "Euro",
"symbol": "€",
"decimals": 2
},
"totalSendingAmount": 10100,
"totalReceivingAmount": 9200,
"exchangeRate": 0.92,
"expiresAt": "2023-09-01T14:30:00Z",
"feesIncluded": 100,
"counterpartyInformation": {
"FULL_NAME": "Jane Receiver",
"BIRTH_DATE": "1990-01-01"
},
"paymentInstructions": [
{
"instructionsNotes": "Include reference code in transfer memo",
"accountOrWalletInfo": {
"accountType": "US_ACCOUNT",
"accountNumber": "1234567890",
"routingNumber": "021000021",
"bankName": "Chase Bank",
"referenceCode": "REF123456"
}
}
]
}
Use the paymentInstructions from the quote to execute a payment through your banking provider. Be sure to include the provided reference code if applicable.
You can track the status of your payment by polling the payment status endpoint or waiting for the webhook notification. To poll the payment status, use the following endpoint:
GET /quotes/Quote:019542f5-b3e7-1d02-0000-000000000006
Response:
{
"quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006",
"sendingCurrency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
},
"receivingCurrency": {
"code": "EUR",
"name": "Euro",
"symbol": "€",
"decimals": 2
},
"totalSendingAmount": 10100,
"totalReceivingAmount": 9200,
"exchangeRate": 0.92,
"expiresAt": "2023-09-01T14:30:00Z",
"feesIncluded": 100,
"counterpartyInformation": {
"FULL_NAME": "Jane Receiver",
"BIRTH_DATE": "1990-01-01"
},
"paymentInstructions": [{
"instructionsNotes": "Include reference code in transfer memo",
"accountOrWalletInfo": {
"accountType": "US_ACCOUNT",
"accountNumber": "1234567890",
"routingNumber": "021000021",
"bankName": "Chase Bank",
"referenceCode": "REF123456"
}
}],
"status": "COMPLETED",
"transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000005"
}
When the payment status changes (to completed or failed), your platform will receive a webhook notification at your configured webhook endpoint:
{
"transaction": {
"transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000005",
"status": "COMPLETED",
"type": "OUTGOING",
"senderUmaAddress": "[email protected]",
"receiverUmaAddress": "[email protected]",
"sentAmount": {
"amount": 10550,
"currency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
}
},
"receivedAmount": {
"amount": 9706,
"currency": {
"code": "EUR",
"name": "Euro",
"symbol": "€",
"decimals": 2
}
},
"userId": "User:019542f5-b3e7-1d02-0000-000000000001",
"platformUserId": "9f84e0c2a72c4fa",
"settledAt": "2023-08-15T14:30:00Z",
"createdAt": "2023-08-15T14:25:18Z",
"description": "Payment for invoice #1234",
"exchangeRate": 0.92,
"quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006",
},
"timestamp": "2023-08-15T14:32:00Z",
"webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007",
"type": "OUTGOING_PAYMENT"
}
This guide outlines the process for platforms to receive payments using the Grid API.
The following sequence diagram illustrates the interaction between your platform and the Grid API when receiving payments:
sequenceDiagram
participant Sender as External Sender
participant Grid as Grid API
participant Client as Your Platform
participant Bank as Banking Provider
Note over Client, Grid: One-time setup
Client->>Grid: PATCH /config (set domain, webhook URL)
Grid-->>Client: Configuration saved
Client->>Grid: POST /users (register users with bank info)
Grid-->>Client: User registered
Note over Sender, Grid: Payment initiated by sender
Sender->>Grid: Initiates payment to UMA address
Grid->>Client: Webhook: INCOMING_PAYMENT (PENDING)
alt Synchronous Approval/Rejection
alt Payment approved
Client-->>Grid: HTTP 200 OK (approve payment)
Grid->>Bank: Execute payment to user's bank account
Grid->>Client: Webhook: INCOMING_PAYMENT (COMPLETED)
Client-->>Grid: HTTP 200 OK (acknowledge completion)
else Payment rejected
Client-->>Grid: HTTP 403 Forbidden with rejection reason
Grid->>Sender: Payment rejected notification
end
else Asynchronous Processing (within 5 seconds)
Client-->>Grid: HTTP 202 Accepted
Client->>Grid: /transactions/{transactionId}/approve or /reject
alt Approved
Grid->>Bank: Execute payment to user's bank account
Grid->>Client: Webhook: INCOMING_PAYMENT (COMPLETED)
Client-->>Grid: HTTP 200 OK (acknowledge completion)
else Rejected
Grid->>Sender: Payment rejected notification
end
end
The process consists of five main steps:
Configure your platform settings (if you haven't already in the onboarding process), including the required counterparty information.
PATCH /config
Request body:
{
"umaDomain": "mycompany.com",
"webhookEndpoint": "https://api.mycompany.com/webhooks/uma",
"supportedCurrencies": [
{
"currencyCode": "USD",
"minAmount": 100,
"maxAmount": 1000000,
"requiredCounterpartyFields": [
{
"name": "FULL_NAME",
"mandatory": true
},
{
"name": "BIRTH_DATE",
"mandatory": true
}
]
}
]
}
First, register your users in the system so they can receive payments via UMA.
POST /users
Request body:
{
"umaAddress": "[email protected]",
"platformUserId": "9f84e0c2a72c4fa",
"userType": "INDIVIDUAL",
"fullName": "John Sender",
"birthDate": "1985-06-15",
"address": {
"line1": "123 Pine Street",
"line2": "Unit 501",
"city": "Mexico City",
"state": "Mexico City",
"postalCode": "01000",
"country": "MX"
},
"bankAccountInfo": {
"accountType": "CLABE",
"accountNumber": "123456789012345678",
"bankName": "Banco de México"
}
}
Configure your webhook endpoints to receive notifications about incoming payments. You'll need to implement the webhook endpoints on your server. Remember to validate webhook signatures to ensure they are authentic.
When someone initiates a payment to one of your users' UMA addresses, you'll receive a webhook call with a pending transaction:
{
"transaction": {
"transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000005",
"status": "PENDING",
"type": "INCOMING",
"senderUmaAddress": "[email protected]",
"receiverUmaAddress": "[email protected]",
"receivedAmount": {
"amount": 50000,
"currency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
}
},
"userId": "User:019542f5-b3e7-1d02-0000-000000000001",
"platformUserId": "9f84e0c2a72c4fa",
"counterpartyInformation": {
"FULL_NAME": "John Sender",
"BIRTH_DATE": "1985-06-15"
},
"reconciliationInstructions": {
"reference": "REF-123456789"
}
},
"timestamp": "2023-08-15T14:32:00Z",
"webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007",
"type": "INCOMING_PAYMENT"
}
To approve the payment synchronously, respond with a 200 OK status.
To reject the payment synchronously, respond with a 403 Forbidden status and a JSON body with the following fields (see API spec for error codes):
{
"code": "REJECTED_BY_PLATFORM",
"message": "Payment rejected due to compliance policy.",
"reason": "FAILED_COUNTERPARTY_CHECK"
}
Alternatively, to process the payment asynchronously, return a 202 Accepted response. Then, you must call the /transactions/{transactionId}/approve or /transactions/{transactionId}/reject endpoint within 5 seconds. Synchronous approval/rejection is preferred where possible.
When the payment completes, your webhook endpoint will receive another notification:
{
"transaction": {
"transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000005",
"status": "COMPLETED",
"type": "INCOMING",
"senderUmaAddress": "[email protected]",
"receiverUmaAddress": "[email protected]",
"receivedAmount": {
"amount": 50000,
"currency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
}
},
"userId": "User:019542f5-b3e7-1d02-0000-000000000001",
"platformUserId": "9f84e0c2a72c4fa",
"settledAt": "2023-08-15T14:30:00Z",
"createdAt": "2023-08-15T14:25:18Z",
"description": "Payment for services",
"reconciliationInstructions": {
"reference": "REF-123456789"
},
},
"timestamp": "2023-08-15T14:32:00Z",
"webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007",
"type": "INCOMING_PAYMENT"
}
The API supports both individual and business users, with different required information for each:
Required information:
Required information:
Additional optional business information:
When creating or updating users, the userType field must be specified as either INDIVIDUAL or BUSINESS, and the appropriate properties for that user type must be provided.
The API supports various bank account formats based on country:
All webhooks sent by the Grid API include a signature in the X-Grid-Signature header, which allows you to verify the authenticity of the webhook. This is critical for security, as it ensures that only legitimate webhooks from Grid are processed by your system.
To verify the signature:
X-Grid-Signature header contains a JSON string, for example: {"v": "1", "s": "BASE64_ENCODED_SIGNATURE"}. Parse this JSON and extract the Base64-encoded signature string from the s field.If the signature verification succeeds, the webhook is authentic. If not, it should be rejected (e.g., with an HTTP 401 Unauthorized response).
Note: The following examples assume you have the Grid P-256 public key (PEM encoded) stored in an environment variable GRID_PUBLIC_KEY_PEM.
This example uses the built-in crypto module available in Node.js.
const crypto = require('crypto');
const express = require('express');
const app = express();
// The Grid P-256 public key (PEM encoded), provided during integration
const gridPublicKeyPem = process.env.GRID_PUBLIC_KEY_PEM;
if (!gridPublicKeyPem) {
console.error('Grid public key (PEM) is not configured. Please set GRID_PUBLIC_KEY_PEM environment variable.');
process.exit(1);
}
// Middleware to get the raw body
app.use(express.json({
verify: (req, res, buf, encoding) => {
if (buf && buf.length) {
// Store the raw buffer for hashing, and string for potential logging (careful with PII)
req.rawBodyBuffer = buf;
req.rawBodyString = buf.toString(encoding || 'utf-8');
} else {
req.rawBodyBuffer = Buffer.alloc(0);
req.rawBodyString = '';
}
}
}));
app.post('/webhooks/grid', (req, res) => {
const signatureHeader = req.header('X-Grid-Signature');
if (!signatureHeader) {
console.warn('Signature missing from X-Grid-Signature header');
return res.status(401).json({ error: 'Signature missing' });
}
try {
let signatureBase64;
try {
const signatureObject = JSON.parse(signatureHeader);
if (typeof signatureObject.s !== 'string') {
throw new Error('Signature string (s) not found in JSON header');
}
signatureBase64 = signatureObject.s;
} catch (jsonError) {
// Fallback for plain base64 signature if JSON parsing fails or if it's not an object with 's'
// This fallback might be removed if the JSON structure is strictly enforced.
console.warn('Failed to parse signature header as JSON, trying as plain base64: ', jsonError.message);
if (typeof signatureHeader === 'string' && signatureHeader.length > 0) {
signatureBase64 = signatureHeader;
} else {
return res.status(400).json({ error: 'Invalid signature header format' });
}
}
const publicKey = crypto.createPublicKey({
key: gridPublicKeyPem,
format: 'pem'
});
const signatureBytes = Buffer.from(signatureBase64, 'base64');
// Create a SHA-256 hash of the raw request body
const requestBodyHash = crypto.createHash('sha256').update(req.rawBodyBuffer).digest();
const verify = crypto.createVerify('SHA256');
verify.update(requestBodyHash); // Verify against the hash of the body
// OR, some libraries might expect the original data for certain verify setups:
// verify.update(req.rawBodyBuffer); // If the public key type implies the hashing algorithm.
// For clarity and safety, verifying against the explicit hash is better.
const isValid = verify.verify(publicKey, signatureBytes);
if (!isValid) {
console.warn('Invalid signature');
return res.status(401).json({ error: 'Invalid signature' });
}
// Webhook is verified, process it based on type
const webhookData = req.body; // req.body is the parsed JSON from express.json()
console.log(`Webhook verified and processing type: ${webhookData.type}`);
// Process webhookData.type...
// Example: if (webhookData.type === 'INCOMING_PAYMENT') { /* ... */ }
return res.status(200).json({ received: true });
} catch (error) {
console.error('Error during signature verification:', error.message, error.stack);
return res.status(500).json({ error: 'Signature verification process error' });
}
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Webhook server listening on port ${port}`);
});
This example uses the ecdsa library for P-256 signature verification and hashlib for SHA256 hashing. You may need to install it: pip install ecdsa hashlib.
import os
import base64
import json
import hashlib
from ecdsa import VerifyingKey, NIST256p, BadSignatureError
from flask import Flask, request, jsonify
app = Flask(__name__)
# The Grid P-256 public key (PEM encoded), provided during integration
GRID_PUBLIC_KEY_PEM = os.environ.get('GRID_PUBLIC_KEY_PEM')
if not GRID_PUBLIC_KEY_PEM:
raise ValueError("Grid public key (PEM) is not configured. Please set GRID_PUBLIC_KEY_PEM environment variable.")
try:
# The hashfunc=hashlib.sha256 here is for the key itself if needed, not for the data hash verification method directly.
# The verify method will take the data hash.
GRID_VERIFY_KEY = VerifyingKey.from_pem(GRID_PUBLIC_KEY_PEM, hashfunc=hashlib.sha256)
except Exception as e:
raise ValueError(f"Failed to load Grid public key from PEM: {e}")
@app.route('/webhooks/grid', methods=['POST'])
def handle_webhook():
signature_header = request.headers.get('X-Grid-Signature')
if not signature_header:
print("Signature missing from X-Grid-Signature header")
return jsonify({'error': 'Signature missing'}), 401
try:
signature_base64 = json.loads(signature_header).get('s')
if not isinstance(signature_base64, str):
raise ValueError("Signature string (s) not found or not a string in JSON header")
except (json.JSONDecodeError, ValueError) as e:
# Fallback for plain base64 signature for robustness, though spec implies JSON.
print(f"Could not parse signature header as JSON or 's' field missing/invalid: {e}. Trying as plain base64.")
if isinstance(signature_header, str) and len(signature_header) > 0:
signature_base64 = signature_header
else:
return jsonify({'error': 'Invalid signature header format'}), 400
try:
signature_bytes = base64.b64decode(signature_base64)
except Exception as e:
print(f"Invalid base64 encoding for signature: {e}")
return jsonify({'error': 'Invalid signature encoding'}), 401
# Get raw request body and hash it with SHA-256
request_body_bytes = request.get_data()
request_body_hash_bytes = hashlib.sha256(request_body_bytes).digest()
try:
# The verify method takes the signature and the hash of the data.
# The curve (NIST256p) is inherent in the VerifyingKey object.
# The hashfunc parameter in verify is for the digest algorithm if the key can be used with multiple,
# but for ECDSA it's usually tied to the key or specified at key loading.
# Here, we pass the raw hash.
is_valid = GRID_VERIFY_KEY.verify(signature_bytes, request_body_hash_bytes, hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_der)
# Note: sigdecode=ecdsa.util.sigdecode_der might be needed if the signature is in DER format.
# If the signature is just raw r and s values concatenated, sigdecode might not be needed or a different one used.
# Assuming DER which is common for ECDSA signatures.
except BadSignatureError:
print("Invalid signature")
return jsonify({'error': 'Invalid signature'}), 401
except Exception as e:
print(f"Error during signature verification: {e}")
return jsonify({'error': 'Signature verification process error'}), 500
# Webhook is verified, process it based on type
webhook_data = request.json # This is the parsed JSON from Flask
print(f"Webhook verified and processing type: {webhook_data.get('type')}")
# Process webhook_data.get('type')
# Example: if webhook_data.get('type') == 'INCOMING_PAYMENT':
# print("Processing INCOMING_PAYMENT webhook...")
return jsonify({'received': True}), 200
if __name__ == '__main__':
port = int(os.environ.get("PORT", 3000))
app.run(host='0.0.0.0', port=port)
webhookId field to prevent processing duplicate webhooks.npm installnpm run buildTo generate the documentation, you'll need Node.js (v16 or later) installed.
# Install dependencies
npm install
# Or use make and build all
make install
make build
This will bundle and mirror the full openapi spec into the openapi.yaml file and the mintlify/openapi.yaml file.
You can serve the documentation locally for development purposes:
# Serve Mintlify documentation:
# First install mint if you haven't already
npm i -g mint
mint dev # or make mint
We use Redocly to split and merge OpenAPI schema you can install it with:
npm i -g @redocly/cli@latest
You can merge openapi schema with
redocly bundle openapi/openapi.yaml -o openapi.yaml
We use Redocly to lint the OpenAPI schema and markdown-lint to lint the markdown documentation:
npm run lint
# Or: make lint
This repository includes a Claude Code skill for interacting with the Grid API. The skill enables Claude to execute API operations, answer documentation questions, and guide you through payment workflows.
Before using the skill, build the CLI tool:
cd cli && npm install && npm run build && cd ..
Then configure credentials (see Configuration section below).
When using Claude Code in this repository, invoke the skill with /grid-api or ask questions like:
The skill uses the CLI at cli/dist/index.js to execute operations against the Grid API.
Set your API credentials as environment variables:
export GRID_API_TOKEN_ID="your-token-id"
export GRID_API_CLIENT_SECRET="your-client-secret"
For any questions or issues, contact our team.