> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rebellapp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Call an API via Adding a Signature

> Step-by-step guide to signing API requests and validating response signatures.

Before calling any API, you must sign requests and validate response signatures to ensure message security and integrity.

## Request Signing Process

### Step 1: Prepare Your Private Key

Ensure your private key is ready. It must be a **2048-bit RSA key**.

```javascript theme={null}
// Load private key from environment or secure storage
const privateKey = process.env.REBELL_PRIVATE_KEY;
// Or from file
const privateKey = fs.readFileSync('private_key.pem', 'utf-8');
```

```python theme={null}
from cryptography.hazmat.primitives import serialization

# Load private key
with open('private_key.pem', 'rb') as f:
    private_key = serialization.load_pem_private_key(f.read(), password=None)
```

### Step 2: Construct Content to Sign

Build a string combining the HTTP method, URI, client identifier, timestamp, and request body:

```
<HTTP_METHOD> <HTTP_URI>
<Client-Id>.<Request-Time>.<HTTP_BODY>
```

**Components**:

| Component    | Description             | Example                      |
| ------------ | ----------------------- | ---------------------------- |
| HTTP\_METHOD | The HTTP method         | `POST`                       |
| HTTP\_URI    | The API endpoint path   | `/v1/payments/pay`           |
| Client-Id    | Your assigned client ID | `TEST_5X00000000000000`      |
| Request-Time | ISO 8601 timestamp      | `2024-01-10T12:12:12+01:00`  |
| HTTP\_BODY   | JSON request body       | `{"paymentRequestId":"..."}` |

**Example content to sign**:

```
POST /v1/payments/pay
TEST_5X00000000000000.2024-01-10T12:12:12+01:00.{"paymentRequestId":"REQ_001","paymentAmount":{"currency":"EUR","value":"1000"}}
```

### Step 3: Generate Signature

Apply RSA with SHA-256 algorithm to generate the signature:

<CodeGroup>
  ```javascript Node.js theme={null}
  const crypto = require('crypto');

  function generateSignature(contentToSign, privateKey) {
    const signer = crypto.createSign('RSA-SHA256');
    signer.update(contentToSign);
    const signature = signer.sign(privateKey, 'base64');
    return signature;
  }

  // Build content to sign
  const method = 'POST';
  const uri = '/v1/payments/pay';
  const clientId = 'YOUR_CLIENT_ID';
  const requestTime = new Date().toISOString();
  const body = JSON.stringify(requestBody);

  const contentToSign = `${method} ${uri}\n${clientId}.${requestTime}.${body}`;
  const signature = generateSignature(contentToSign, privateKey);
  ```

  ```python Python theme={null}
  import base64
  from datetime import datetime
  from cryptography.hazmat.primitives import hashes
  from cryptography.hazmat.primitives.asymmetric import padding

  def generate_signature(content_to_sign: str, private_key) -> str:
      signature = private_key.sign(
          content_to_sign.encode('utf-8'),
          padding.PKCS1v15(),
          hashes.SHA256()
      )
      return base64.b64encode(signature).decode('utf-8')

  # Build content to sign
  method = 'POST'
  uri = '/v1/payments/pay'
  client_id = 'YOUR_CLIENT_ID'
  request_time = datetime.utcnow().isoformat() + 'Z'
  body = json.dumps(request_body, separators=(',', ':'))

  content_to_sign = f"{method} {uri}\n{client_id}.{request_time}.{body}"
  signature = generate_signature(content_to_sign, private_key)
  ```
</CodeGroup>

### Step 4: Add Signature to Header

Format and add the signature header to your request:

```
Signature: algorithm=SHA256withRSA, keyVersion=1, signature=<generated_signature>
```

| Field        | Description                                   |
| ------------ | --------------------------------------------- |
| `algorithm`  | Always `SHA256withRSA`                        |
| `keyVersion` | Your key version (provided during onboarding) |
| `signature`  | Base64-encoded signature from Step 3          |

## Complete Request Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.rebellapp.com/v1/payments/pay \
    -H 'Content-Type: application/json' \
    -H 'Client-Id: TEST_5X00000000000000' \
    -H 'Request-Time: 2024-01-10T12:12:12+01:00' \
    -H 'Signature: algorithm=SHA256withRSA, keyVersion=1, signature=BASE64_SIGNATURE_HERE' \
    -d '{
      "paymentRequestId": "REQ_001",
      "paymentAmount": {
        "currency": "EUR",
        "value": "1000"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  const crypto = require('crypto');

  async function callAPI(endpoint, body) {
    const method = 'POST';
    const clientId = process.env.REBELL_CLIENT_ID;
    const keyVersion = '1';
    const privateKey = process.env.REBELL_PRIVATE_KEY;
    const requestTime = new Date().toISOString();
    const bodyString = JSON.stringify(body);

    // Build content to sign
    const contentToSign = `${method} ${endpoint}\n${clientId}.${requestTime}.${bodyString}`;

    // Generate signature
    const signer = crypto.createSign('RSA-SHA256');
    signer.update(contentToSign);
    const signature = signer.sign(privateKey, 'base64');

    // Make request
    const response = await fetch(`https://api.rebellapp.com${endpoint}`, {
      method,
      headers: {
        'Content-Type': 'application/json',
        'Client-Id': clientId,
        'Request-Time': requestTime,
        'Signature': `algorithm=SHA256withRSA, keyVersion=${keyVersion}, signature=${signature}`
      },
      body: bodyString
    });

    return response.json();
  }
  ```

  ```python Python theme={null}
  import json
  import base64
  import requests
  from datetime import datetime
  from cryptography.hazmat.primitives import hashes, serialization
  from cryptography.hazmat.primitives.asymmetric import padding

  def call_api(endpoint: str, body: dict) -> dict:
      method = 'POST'
      client_id = os.environ['REBELL_CLIENT_ID']
      key_version = '1'
      request_time = datetime.utcnow().isoformat() + 'Z'
      body_string = json.dumps(body, separators=(',', ':'))

      # Load private key
      with open('private_key.pem', 'rb') as f:
          private_key = serialization.load_pem_private_key(f.read(), password=None)

      # Build content to sign
      content_to_sign = f"{method} {endpoint}\n{client_id}.{request_time}.{body_string}"

      # Generate signature
      signature = private_key.sign(
          content_to_sign.encode('utf-8'),
          padding.PKCS1v15(),
          hashes.SHA256()
      )
      signature_b64 = base64.b64encode(signature).decode('utf-8')

      # Make request
      response = requests.post(
          f'https://api.rebellapp.com{endpoint}',
          headers={
              'Content-Type': 'application/json',
              'Client-Id': client_id,
              'Request-Time': request_time,
              'Signature': f'algorithm=SHA256withRSA, keyVersion={key_version}, signature={signature_b64}'
          },
          data=body_string
      )

      return response.json()
  ```
</CodeGroup>

## Response Signature Validation

Always verify the response signature before processing the data.

### Construct Content to Validate

Mirror the request signing process using response data:

```
<HTTP_METHOD> <HTTP_URI>
<Client-Id>.<Response-Time>.<HTTP_BODY>
```

### Verify Signature

<CodeGroup>
  ```javascript Node.js theme={null}
  function verifySignature(method, uri, clientId, responseTime, body, signature, publicKey) {
    const contentToVerify = `${method} ${uri}\n${clientId}.${responseTime}.${body}`;

    const verifier = crypto.createVerify('RSA-SHA256');
    verifier.update(contentToVerify);

    return verifier.verify(publicKey, signature, 'base64');
  }

  // Usage
  const isValid = verifySignature(
    'POST',
    '/v1/payments/pay',
    clientId,
    response.headers['response-time'],
    JSON.stringify(response.body),
    extractSignature(response.headers['signature']),
    rebellPublicKey
  );

  if (!isValid) {
    throw new Error('Invalid response signature');
  }
  ```

  ```python Python theme={null}
  from cryptography.hazmat.primitives.asymmetric import padding
  from cryptography.hazmat.primitives import hashes

  def verify_signature(method, uri, client_id, response_time, body, signature_b64, public_key):
      content_to_verify = f"{method} {uri}\n{client_id}.{response_time}.{body}"
      signature = base64.b64decode(signature_b64)

      try:
          public_key.verify(
              signature,
              content_to_verify.encode('utf-8'),
              padding.PKCS1v15(),
              hashes.SHA256()
          )
          return True
      except Exception:
          return False
  ```
</CodeGroup>

## Key Points

<Checks>
  * Timestamp accuracy is critical - use ISO 8601 format with timezone
  * Use 2048-bit RSA keys exclusively
  * Always validate response signatures before processing
  * Store private keys securely, never in client-side code
  * Use consistent JSON serialization (no extra whitespace)
</Checks>

<Warning>
  **Security Alert**: Never log or expose the full signature content or private key. If you need to debug signature issues, log only non-sensitive parts like the method and URI.
</Warning>
