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

# Get Public Key

> Retrieves the public key for encrypting credit card information

## Overview

This endpoint retrieves the current public key used for encrypting credit card data before sending it to Firmly's payment endpoints. The key supports multiple formats to accommodate different encryption libraries and platforms.

<Note>
  The public key is rotated periodically for security. Always fetch the current key before encrypting payment data rather than caching it for extended periods.
</Note>

## Authentication

This endpoint requires no authentication and is publicly accessible.

## Query Parameters

<ParamField query="format" default="JWK" type="string">
  The format of the public key to return

  **Supported formats:**

  * `JWK` - JSON Web Key format (default)
  * `PEM` - Privacy Enhanced Mail format
  * `RSA` - PEM using RSAPublicKey format
</ParamField>

## Response Headers

<ResponseField name="x-firmly-kid" type="string">
  The key identifier (kid) - present for all formats
</ResponseField>

## Response Formats

### JWK Format (Default)

Returns a JSON Web Key with the following properties:

<ResponseField name="kid" type="string">
  Key identifier/version
</ResponseField>

<ResponseField name="kty" type="string">
  Key type (always "RSA")
</ResponseField>

<ResponseField name="n" type="string">
  RSA modulus component (Base64URL encoded)
</ResponseField>

<ResponseField name="e" type="string">
  RSA exponent component (Base64URL encoded)
</ResponseField>

<ResponseField name="use" type="string">
  Key usage (always "enc" for encryption)
</ResponseField>

### PEM Format

Returns the public key in PEM format as plain text:

* Content-Type: `text/plain`
* Key ID available in `x-firmly-kid` header
* Standard PEM header/footer with base64 encoded key

### RSA Format

Returns the public key in RSA-specific PEM format:

* Content-Type: `text/plain`
* Key ID available in `x-firmly-kid` header
* Uses RSA PUBLIC KEY header/footer

## Code Examples

<CodeGroup>
  ```javascript Fetch JWK Format theme={null}
  // Fetch public key in JWK format (default)
  async function getPublicKey() {
    const response = await fetch('https://cc.firmly.work/api/v1/payment/key');
    if (!response.ok) {
      throw new Error(`Failed to fetch public key: ${response.statusText}`);
    }
    
    const publicKey = await response.json();
    console.log('Key ID:', publicKey.kid);
    console.log('Key Type:', publicKey.kty);
    
    return publicKey;
  }

  // Use with Web Crypto API
  async function importKey(jwk) {
    return await crypto.subtle.importKey(
      'jwk',
      jwk,
      {
        name: 'RSA-OAEP',
        hash: 'SHA-256'
      },
      false,
      ['encrypt']
    );
  }
  ```

  ```python Fetch PEM Format theme={null}
  import requests

  def get_public_key_pem():
      """Fetch public key in PEM format"""
      response = requests.get(
          'https://cc.firmly.work/api/v1/payment/key',
          params={'format': 'PEM'}
      )
      
      if response.status_code != 200:
          raise Exception(f"Failed to fetch public key: {response.status_code}")
      
      # Get key ID from header
      key_id = response.headers.get('x-firmly-kid')
      public_key_pem = response.text
      
      print(f"Key ID: {key_id}")
      return public_key_pem, key_id

  # Use with cryptography library
  from cryptography.hazmat.primitives import serialization
  from cryptography.hazmat.backends import default_backend

  def load_public_key(pem_data):
      return serialization.load_pem_public_key(
          pem_data.encode('utf-8'),
          backend=default_backend()
      )
  ```

  ```bash cURL Examples theme={null}
  # Get JWK format (default)
  curl https://cc.firmly.work/api/v1/payment/key

  # Get PEM format
  curl https://cc.firmly.work/api/v1/payment/key?format=PEM

  # Get RSA format
  curl https://cc.firmly.work/api/v1/payment/key?format=RSA

  # Get PEM format and extract key ID from header
  curl -i https://cc.firmly.work/api/v1/payment/key?format=PEM | grep x-firmly-kid
  ```

  ```php PHP Example theme={null}
  <?php
  // Fetch public key in JWK format
  function getPublicKey() {
      $url = 'https://cc.firmly.work/api/v1/payment/key';
      
      $response = file_get_contents($url);
      if ($response === false) {
          throw new Exception('Failed to fetch public key');
      }
      
      $publicKey = json_decode($response, true);
      
      echo "Key ID: " . $publicKey['kid'] . "\n";
      echo "Key Type: " . $publicKey['kty'] . "\n";
      
      return $publicKey;
  }

  // Fetch PEM format with headers
  function getPublicKeyPEM() {
      $context = stream_context_create([
          'http' => ['method' => 'GET']
      ]);
      
      $url = 'https://cc.firmly.work/api/v1/payment/key?format=PEM';
      $response = file_get_contents($url, false, $context);
      
      // Extract key ID from headers
      foreach ($http_response_header as $header) {
          if (stripos($header, 'x-firmly-kid:') === 0) {
              $keyId = trim(substr($header, 13));
              break;
          }
      }
      
      return ['pem' => $response, 'kid' => $keyId];
  }
  ?>
  ```
</CodeGroup>

## Response Examples

### JWK Format Response

```json theme={null}
{
  "kid": "a81b2d581f2a42c09143eb6fdb918fff",
  "kty": "RSA",
  "n": "yURqBPP1k_kwMp8AiHeZya7zgO9ZulKrNvFYcQK2eIvkbl7VlhxYt6bnJ0urrUrJbuM_bbRg3yiwXtAN_BsHWTm6JwWSjRx3PQMIm0Yb-HGj2YM6moJ9YFACqtZB2zjkE98Q_TOhfAnYuoSIPsY3k9U1iJmi6gpaZ7E01QFGoRlAwB55yMETl3UT7uodGLRPBz_JGhRuDCJ1dVEfzcojUxOt7FFbRIGDGQzMTvmskRID3N50z6UwJOFwmP6N17qIMYCbr3IQg0fU75HsL-lChpA8m-EnvK0hL4CcNnBVqupxzhsKq2SSLigNBFC6J4gs3mV7L7qu1Q8u_Cg5tGVZiQ",
  "e": "AQAB",
  "use": "enc"
}
```

### PEM Format Response

```
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyURqBPP1k/kwMp8AiHeZ
ya7zgO9ZulKrNvFYcQK2eIvkbl7VlhxYt6bnJ0urrUrJbuM/bbRg3yiwXtAN/BsH
WTm6JwWSjRx3PQMIm0Yb+HGj2YM6moJ9YFACqtZB2zjkE98Q/TOhfAnYuoSIPsY3
k9U1iJmi6gpaZ7E01QFGoRlAwB55yMETl3UT7uodGLRPBz/JGhRuDCJ1dVEfzcoj
UxOt7FFbRIGDGQzMTvmskRID3N50z6UwJOFwmP6N17qIMYCbr3IQg0fU75HsL+lC
hpA8m+EnvK0hL4CcNnBVqupxzhsKq2SSLigNBFC6J4gs3mV7L7qu1Q8u/Cg5tGVZ
iQIDAQAB
-----END PUBLIC KEY-----
```

### RSA Format Response

```
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAyURqBPP1k/kwMp8AiHeZya7zgO9ZulKrNvFYcQK2eIvkbl7VlhxY
t6bnJ0urrUrJbuM/bbRg3yiwXtAN/BsHWTm6JwWSjRx3PQMIm0Yb+HGj2YM6moJ9
YFACqtZB2zjkE98Q/TOhfAnYuoSIPsY3k9U1iJmi6gpaZ7E01QFGoRlAwB55yMET
l3UT7uodGLRPBz/JGhRuDCJ1dVEfzcojUxOt7FFbRIGDGQzMTvmskRID3N50z6Uw
JOFwmP6N17qIMYCbr3IQg0fU75HsL+lChpA8m+EnvK0hL4CcNnBVqupxzhsKq2SS
LigNBFC6J4gs3mV7L7qu1Q8u/Cg5tGVZiQIDAQAB
-----END RSA PUBLIC KEY-----
```

## Usage with Encryption Libraries

### JavaScript (Web Crypto API)

```javascript theme={null}
async function encryptWithJWK(data, jwk) {
  // Import the JWK
  const publicKey = await crypto.subtle.importKey(
    'jwk',
    jwk,
    {
      name: 'RSA-OAEP',
      hash: 'SHA-256'
    },
    false,
    ['encrypt']
  );
  
  // Encrypt the data
  const encrypted = await crypto.subtle.encrypt(
    { name: 'RSA-OAEP' },
    publicKey,
    new TextEncoder().encode(data)
  );
  
  // Convert to base64
  return btoa(String.fromCharCode(...new Uint8Array(encrypted)));
}
```

### Python (cryptography)

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

def encrypt_with_pem(data, pem_key):
    # Load the PEM key
    public_key = serialization.load_pem_public_key(
        pem_key.encode('utf-8')
    )
    
    # Encrypt the data
    encrypted = public_key.encrypt(
        data.encode('utf-8'),
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    
    # Return base64 encoded
    return base64.b64encode(encrypted).decode('utf-8')
```

### Node.js (node-jose)

```javascript theme={null}
const jose = require('node-jose');

async function encryptWithJWK(data, jwk) {
  // Create key from JWK
  const key = await jose.JWK.asKey(jwk);
  
  // Encrypt the data
  const encrypted = await jose.JWE.createEncrypt({
    format: 'compact',
    contentAlg: 'A256GCM'
  }, key)
    .update(JSON.stringify(data))
    .final();
  
  return encrypted;
}
```

## Best Practices

<Warning>
  * **Key Rotation**: The public key may be cached for up to. 15 minutes. Upon invalid key error, fetch the key and retry one time. Key validation should be removed
  * **Format Selection**: Choose the format that works best with your encryption library
  * **Error Handling**: Always handle network errors when fetching the key
  * **Key Validation**: Verify the key ID matches between encryption and submission
</Warning>

## Common Use Cases

1. **Credit Card Encryption**: Primary use is for encrypting credit card data for payment endpoints
2. **Tokenization**: Used with payment tokenization endpoints
3. **Secure Data Transmission**: Any sensitive data sent to Firmly payment endpoints

## Checkout Flow Integration

This endpoint is the first step in the secure payment flow:

<Steps>
  <Step title="Fetch Public Key">
    Call this endpoint to get the current encryption key
  </Step>

  <Step title="Encrypt Credit Card">
    Use the public key to encrypt card data according to your chosen format
  </Step>

  <Step title="Complete Order">
    Send encrypted card data to one of the complete-order endpoints
  </Step>
</Steps>

### Complete Example Flow

```javascript theme={null}
// 1. Get the public key
const publicKeyResponse = await fetch('https://cc.firmly.work/api/v1/payment/key');
const publicKey = await publicKeyResponse.json();

// 2. Encrypt credit card data
const encryptedCard = await encryptCard({
  number: "4111111111111111",
  name: "John Smith",
  verification_value: "123",
  month: "12",
  year: "2025"
}, publicKey);

// 3. Complete the order
const orderResponse = await fetch('https://cc.firmly.work/api/v1/payment/domains/staging.luma.gift/complete-order', {
  method: 'POST',
  headers: {
    'x-firmly-authorization': authToken,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    encrypted_card: encryptedCard,
    billing_info: billingInfo
  })
});

const order = await orderResponse.json();
```

## Related Endpoints

* [Complete Order (Simple Cart)](/api-reference/payment/complete-order-v1) - Complete checkout for Simple Cart API
* [Complete Order (Full Cart)](/api-reference/payment/complete-order-v2) - Complete checkout for Full Cart API
* [Place Order (Simple Cart)](/api-reference/payment/place-order-v1) - Create cart and order in one call
* [Place Order (Full Cart)](/api-reference/payment/place-order-v2) - Create cart and order with advanced features
