# 2. API Response

Once the verification by the issuer is completed (ex: KYC verification, AML verification, etc..), the issuers is responsible for generating a standardized API response.

{% hint style="info" %}
[Attributes](/passport-issuer/additional-information/passport-attributes.md) in the Quadrata Passport are standardized to enable interoperability between issuers.
{% endhint %}

### Response API Payload

```
{
  'contractParameters': {
     'account': '',
     'attrKeys': [],
     'attrValues': [],
     'attrTypes' [],
     'fee': '',
     'verifiedAt': '',
     'issuedAt': '',
     'chainId': '',
     'signature': '',
     'did': '',
  },
  
   // Remaining Issuer specific Response Payload
}
```

#### **account**

Wallet address of the user to verify&#x20;

#### attrKeys

List of attribute identifiers linking the attribute type and the account.

{% hint style="warning" %}
This list has to be the same length as *attrValues* and *attrTypes* and in the same exact order
{% endhint %}

{% tabs %}
{% tab title="Nodejs" %}

```javascript

const { utils } = require("ethers");
import { BytesLike } from '@ethersproject/bytes';

const computeAttrKeys = (account: string, attrTypes: BytesLike[]) => {
    let attrKeys: string[] = [];
    let attrKey: str;
    
    attrTypes.forEach((attrType) => {
        // For attributes set at the wallet address level (ex: COUNTRY, IS_BUSINESS, DID)
        attrKey = utils.keccak256(
        utils.defaultAbiCoder.encode(["address", "bytes32"]),
        [account, attrType]
        );
    
        // ------ OR -------- //
    
        // For attributes grouped by DID (ex: AML)
        attrKey = utils.keccak256(
            utils.defaultAbiCoder.encode(["bytes32", "bytes32"]),
            [did, attrType]
        ); 
        attrKeys.push(attrKey)
    }
    
    return attrKeys;
}

const account = "0x......"; // User Wallet Address
const attrTypes = [utils.id("COUNTRY"), utils.id("AML")]; // List of attributes to attest

const attrKeys = computeAttrKeys(account, attrTypes)
```

{% endtab %}

{% tab title="Python" %}

```python
from web3 import Web3  # type: ignore

def compute_attr_keys(accountAddress, attrTypes):
    checksum_account = bytes.fromhex(
        Web3.toChecksumAddress(accountAddress)[2:]
    ).rjust(32, b'\0')
    
    attrKeys = []

    for attrType in attrTypes:
        attrKey = Web3.solidityKeccak(
            ['bytes32', 'bytes32'], 
            [
                checksum_account, 
                attrType
            ]
        )
        attrKeys.append(attrKey)
        
    return attrKeys
    

account = "0x........"  # Wallet Address
attrTypes = [Web3.solidityKeccak(['bytes32'], ["AML".encode()]).hex()]
attrKeys = compute_attr_keys(account, attrTypes)
return attrKeys
```

{% endtab %}
{% endtabs %}

#### attrValues

List of attributes values. See [Passport Attributes](/passport-issuer/additional-information/passport-attributes.md) for more information.

{% hint style="warning" %}
This list has to be the same length as *attrValues* and *attrTypes* and in the same exact order
{% endhint %}

#### attrTypes

List of attributes types (ex: `[utils.id("COUNTRY"), utils.id("AML")]`)

{% hint style="warning" %}
This list has to be the same length as *attrValues* and *attrTypes* and in the same exact order
{% endhint %}

#### fee

Fee in the native token ($ETH for Ethereum, $MATIC for Polygon) for attaching the new [attributes](/passport-issuer/additional-information/passport-attributes.md) being attested by the issuers. The fee is in [Wei](https://academy.binance.com/en/glossary/wei).

{% hint style="info" %}
100% of the fee is redistributed to the issuer.
{% endhint %}

#### verifiedAt

Unix Epoch (in seconds) representing the date when the attributes has been verified by the issuer.

#### issuedAt

Unix Epoch (in seconds) representing the date when the attributes signature has been generated - allowing the user to mint their passport

#### chainId

Blockchain Network chain Id (ex: 1 for Ethereum Mainnet, 137 for Polygon). \
See [list of chain ID](https://chainlist.org/).

#### signature

ECDSA Signature signed by the issuer to generate a verifiable proof of the [attributes](/passport-issuer/additional-information/passport-attributes.md) attested

{% tabs %}
{% tab title="Nodejs" %}

```javascript
const { Signer, DataHexString, utils, Wallet } = require("ethers");
import { BytesLike } from '@ethersproject/bytes';


export const signAttributes = async (
  accountAddress: string,
  issuer: typeof Signer,
  attrKeys: BytesLike[],
  attrValues: BytesLike[],
  verifiedAt: number,
  issuedAt: number,
  fee: any,
  passportAddress: string,
  chainId: number,
  did: BytesLike = utils.constants.HashZero,
): Promise<typeof DataHexString> => {
  const attrKeys: string[] = [];
  const attrValues: string[] = [];

  const hash = utils.keccak256(
    utils.defaultAbiCoder.encode(
      [
        "address",
        "bytes32[]",
        "bytes32[]",
        "bytes32",
        "uint256",
        "uint256",
        "uint256",
        "uint256",
        "address",
      ],
      [
        accountAddress,
        attrKeys,
        attrValues,
        did,
        verifiedAt,
        issuedAt,
        fee,
        chainId,
        passportAddress,
      ]
    )
  );

  const sig = await issuer.signMessage(utils.arrayify(hash));

  return sig;
};

const accountAddress = "0x......";  // Wallet address of the user
const issuer = new Wallet(ISSUER_PRIVATE_KEY); // Loading issuer account from their private key
const attrTypes = [utils.id("COUNTRY"), utils.id("AML")];  // List of attributes types
const attrKeys = computeAttrKeys(accountAddress, attrTypes); // List of attributes Identifiers
const attrValues = [utils.id("US"), utils.hexZeroPad("0x01", 32)]; // List of attributes values
const verifiedAt = Math.floor(new Date().getTime() / 1000) - 60; // we substract 60 seconds to avoid errors during minting passport with long block time
const issuedAt = Math.floor(new Date().getTime() / 1000) - 60; // we substract 60 seconds to avoid errors during minting passport with long block time
const passportAddress = "0x2e779749c40CC4Ba1cAB4c57eF84d90755CC017d";
const chainId = 1; // 1 for Ethereum Mainnet
const fee = utils.parseEther("0.001"); // Fee paid by the user to the issuer in then native blokcchain currency (ex: ETH for Ethereum, MATIC for Polygon)

const signature = await signAttributes(
  accountAddress,
  issuer,
  attrKeys,
  attrValues,
  verifiedAt,
  issuedAt, 
  fee,
  passportAddress,
  chainId
);
```

{% endtab %}

{% tab title="Python" %}

```python
import codecs
import eth_abi
from web3 import Web3  # type: ignore
from eth_account.messages import encode_defunct

W3_TIMEOUT = 60
NETWORK_URI = "" # INFURA_RPC_NODE
ISSUER_PRIVATE_KEY = "" # ISSUER__PRIVATE_KEY] 

def _hexed_checksum_address(self, address) -> bytes:
    return bytes.fromhex(Web3.toChecksumAddress(address)[2:]).rjust(32, b'\0')

def generate_signature(
    account_address: str,
    attr_keys: str[],
    attr_values: str[],
    verified_at: int,
    issued_at: int,
    mint_fee: int,
    passport_address: str,
    chain_id: int,
    did: str = web3.constants.HASH_ZERO[2:]
) -> str:
    
    abiEncoded = eth_abi.encode_abi(
        [
            'bytes32',
            'bytes32[]',
            'bytes32[]',
            'bytes32',
            'uint256',
            'uint256',
            'uint256',
            'uint256',
            'bytes32',
        ],
        [
            _hexed_checksum_address(account_address),
            attr_keys,
            attr_values,
            did, 
            verified_at,
            issued_at,
            mint_fee, # fee in wei
            chain_id, # we will support mainnet/goerli or matic/mumbai
            _hexed_checksum_address(passport_address), # see our docs, this varies depending on env/chain
        ]
    )

    digest_to_sign = Web3.solidityKeccak(['bytes32'], [('0x' + abiEncoded.hex())]).hex()
    decoder = codecs.getdecoder('hex_codec')

    w3 = Web3(Web3.HTTPProvider(NETWORK_URI, request_kwargs={'timeout': W3_TIMEOUT}))
    return w3.eth.account.sign_message(
        encode_defunct(hexstr=encoded_msg),
        private_key=decoder(str.encode(ISSUER_PRIVATE_KEY))[0],
    ).signature.hex()
```

{% endtab %}
{% endtabs %}

#### did (Optional)&#x20;

The Decentralized Identifier for the wallet holders. This field is only required for passport issuers performing documentary KYC.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.quadrata.com/passport-issuer/integration-developers/attesting-attributes/2.-api-response.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
