# Manage the Address Book API Users can propose address book changes. Address book changes are subject to the approval of the admin quorum. ## Create a new contact To create a new contact, call the [Create Contact](/api/openapi/address-book/create_contact_api_v1_addressbook_contacts_post) endpoint. In the request, specify the `name` and the `address` of the new contact, the chain `type` of the contact (for example, `evm`, `solana`, `utxo`, and so on). Optionally, if you want the contact to be valid only for specific chains (within the specified chain type) or specific assets, you can specify a list of `chains` (for example, `evm_1,` `solana_mainnet`, and so on) or `assets` this contact should be valid for. Since address book changes are sensitive operations, they require [request signing](/developers/authentication#request-signing). For example, to add a Bitcoin address to the address book: Shell ```shell #!bash ACCESS_TOKEN='' PRIVATE_KEY_FILE='' ENDPOINT='' BODY='' TIMESTAMP="$(($(date +%s) * 1000))" SIGNATURE="$(echo -n "${ENDPOINT}|${TIMESTAMP}|${BODY}" | openssl dgst -sha256 -sign ${PRIVATE_KEY_FILE} | base64 | tr -d \\n)" echo -n "${ENDPOINT}|${TIMESTAMP}|${BODY}" curl -v "https://api.fordefi.com${ENDPOINT}" \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -H "x-signature: ${SIGNATURE}" \ -H "x-timestamp: ${TIMESTAMP}" \ -d "${BODY}" ``` Python ```py import requests import base64 import json import datetime import ecdsa import hashlib access_token = "" private_key_file = "" path = "" request_json = timestamp = datetime.datetime.now().strftime("%s") request_body = json.dumps(request_json) payload = f"{path}|{timestamp}|{request_body}" with open(private_key_file, "r") as f: signing_key = ecdsa.SigningKey.from_pem(f.read()) signature = signing_key.sign( data=payload.encode(), hashfunc=hashlib.sha256, sigencode=ecdsa.util.sigencode_der ) resp_tx = requests.post( f"https://api.fordefi.com{path}", headers={ "Authorization": f"Bearer {access_token}", "x-signature": base64.b64encode(signature), "x-timestamp": timestamp.encode(), }, data=request_body, ) ``` Javascript ```js const crypto = require("crypto"); const fs = require('fs'); const accessToken = "" const privateKeyFile = "" const path = "" const requestJson = const gatewayHost = "api.fordefi.com" const requestBody = JSON.stringify(requestJson) const timestamp = new Date().getTime(); const payload = `${path}|${timestamp}|${requestBody}`; const secretPem = fs.readFileSync(privateKeyFile, 'utf8'); const privateKey = crypto.createPrivateKey(secretPem); const sign = crypto.createSign('SHA256').update(payload, 'utf8').end(); const signature = sign.sign(privateKey, 'base64'); fetch(`https://${gatewayHost}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json', "Authorization": `Bearer ${accessToken}`, 'X-Timestamp': timestamp, 'X-Signature': signature, }, body: requestBody, }).then((response) => response.text()) .then((json) => console.log(json)); ``` Rust ```rust // Using crate p256 = { version = "0.12.0", features = ["pem"] } use base64::{engine::general_purpose, Engine as _}; use p256::ecdsa::{signature::Signer, Signature, SigningKey}; use std::fs; use std::time::{SystemTime, UNIX_EPOCH}; // This is the main function fn main() -> Result<(), Box> { let access_token = ""; let private_key_file = ""; let path = ""; let request_body = r#" "#; let gateway_host = "api.fordefi.com"; let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_millis() .to_string(); let payload = format!("{path}|{timestamp}|{request_body}"); let priv_pem = fs::read_to_string(private_key_file).expect("Failed to read pem file"); let private_key = p256::SecretKey::from_sec1_pem(&priv_pem).expect("Failed to decode pem key"); let signing_key: SigningKey = private_key.into(); let signature: Signature = signing_key.sign(payload.as_bytes()); let formatted_signature = general_purpose::STANDARD.encode(signature.to_der().to_bytes()); let client = reqwest::blocking::Client::new(); let res = client .post(format!("https://{gateway_host}{path}")) .body(request_body) .bearer_auth(access_token) .header("Content-Type", "application/json") .header("X-Timestamp", timestamp) .header("X-Signature", formatted_signature) .send()? .text()?; println!("Result: {res}"); Ok(()) } ``` ## Control the scope of the address An address can be scoped to a single chain, all chains of a particular type (for example, EVM), or a specific asset. For example: All EVM Chains ```json { "name": "My EVM Contact", "type": "evm", "address": "0x3fEBb139Ba00332E0B2DE6994B0bdAA505bf318D", "chains": [] } ``` Single EVM Chain ```json { "name": "My Ethereum Contact", "type": "evm", "address": "0x3fEBb139Ba00332E0B2DE6994B0bdAA505bf318D", "chains": ["evm_1"] } ``` USDC Only ```json { "name": "My USDC-only Ethereum Contact", "type": "evm", "address": "0x3fEBb139Ba00332E0B2DE6994B0bdAA505bf318D", "chains": ["evm_1"], "assets_identifiers": [ { "type": "evm", "details": { "type": "erc20", "token": { "hex_repr": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "chain": "evm_1" } } } ] } ``` ## Update an existing contact To update an existing contact, call the [Edit Contact](/api/openapi/address-book/edit_contact_api_v1_addressbook_contacts__id__proposals_post) endpoint. The edit request has the same format as the creation request, above. ## Abort a proposal After an address book change has been proposed, and until it has been approved by the admin quorum, you can abort the proposal using the [Abort Contact](/api/openapi/address-book/abort_contact_api_v1_addressbook_contacts__id__proposals__proposal_id__abort_post) endpoint. ## List contacts You can list all contacts in your workspace using the [List Contacts](/api/openapi/address-book/list_contacts_api_v1_addressbook_contacts_get) endpoint. This API returns contacts in all states: `pending`, `active`, and `deleted`. If you want to get only the active contacts you can use the `states` query parameter.