|
| 1 | +--- |
| 2 | +slug: /consumers/getting-started |
| 3 | +sidebar_position: 0 |
| 4 | +--- |
| 5 | + |
| 6 | +# Getting Started |
| 7 | + |
| 8 | +You have an airnode URL and want to get signed data. This walkthrough takes you from zero to a verified response. |
| 9 | + |
| 10 | +## Step 1: Check if the airnode is running |
| 11 | + |
| 12 | +```bash |
| 13 | +curl http://airnode.example.com/health |
| 14 | +``` |
| 15 | + |
| 16 | +```json |
| 17 | +{ |
| 18 | + "status": "ok", |
| 19 | + "version": "2.0.0", |
| 20 | + "airnode": "0xd1e98F3Ac20DA5e4da874723517c914a31b0e857" |
| 21 | +} |
| 22 | +``` |
| 23 | + |
| 24 | +Save the `airnode` address. You need it to verify signatures. |
| 25 | + |
| 26 | +## Step 2: Find your endpoint ID |
| 27 | + |
| 28 | +The airnode operator provides the endpoint ID for each API route. It is a `bytes32` hash derived from the endpoint's OIS |
| 29 | +specification. The operator's documentation lists available endpoint IDs and their expected parameters. |
| 30 | + |
| 31 | +## Step 3: Make a request |
| 32 | + |
| 33 | +Call the endpoint with parameters in the JSON body. |
| 34 | + |
| 35 | +```bash |
| 36 | +curl -X POST http://airnode.example.com/endpoints/0x1c3e0fa5ac82e5514e0e9abac98e0b8e6c58b7bea12ae0393e4e4abe64ab9620 \ |
| 37 | + -H "Content-Type: application/json" \ |
| 38 | + -H "X-Api-Key: your-key" \ |
| 39 | + -d '{"parameters":{"ids":"ethereum","vs_currencies":"usd"}}' |
| 40 | +``` |
| 41 | + |
| 42 | +The response contains the data and a signature: |
| 43 | + |
| 44 | +```json |
| 45 | +{ |
| 46 | + "airnode": "0xd1e98F3Ac20DA5e4da874723517c914a31b0e857", |
| 47 | + "endpointId": "0x1c3e0fa5ac82e5514e0e9abac98e0b8e6c58b7bea12ae0393e4e4abe64ab9620", |
| 48 | + "timestamp": 1700000000, |
| 49 | + "data": "0x0000000000000000000000000000000000000000000000d8e29e69b3e1e80000", |
| 50 | + "signature": "0x3a4e...signed" |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +If the endpoint has no encoding configured, you get `rawData` instead of `data`: |
| 55 | + |
| 56 | +```json |
| 57 | +{ |
| 58 | + "airnode": "0xd1e98F3Ac20DA5e4da874723517c914a31b0e857", |
| 59 | + "endpointId": "0x1c3e0fa5ac82e5514e0e9abac98e0b8e6c58b7bea12ae0393e4e4abe64ab9620", |
| 60 | + "timestamp": 1700000000, |
| 61 | + "rawData": { "ethereum": { "usd": 3842.17 } }, |
| 62 | + "signature": "0x3a4e...signed" |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +## Step 4: Verify the signature |
| 67 | + |
| 68 | +Recover the signer address and compare it to the airnode address from `/health`. |
| 69 | + |
| 70 | +```typescript |
| 71 | +import { recoverAddress, hashMessage, keccak256, encodePacked, type Hex } from 'viem'; |
| 72 | + |
| 73 | +const endpointId = '0x1c3e0fa5ac82e5514e0e9abac98e0b8e6c58b7bea12ae0393e4e4abe64ab9620' as Hex; |
| 74 | +const timestamp = 1700000000n; |
| 75 | +const data = '0x0000000000000000000000000000000000000000000000d8e29e69b3e1e80000' as Hex; |
| 76 | +const signature = '0x3a4e...signed' as Hex; |
| 77 | +const expectedAirnode = '0xd1e98F3Ac20DA5e4da874723517c914a31b0e857'; |
| 78 | + |
| 79 | +// Reconstruct the message hash |
| 80 | +const messageHash = keccak256(encodePacked(['bytes32', 'uint256', 'bytes'], [endpointId, timestamp, data])); |
| 81 | + |
| 82 | +// Recover the signer |
| 83 | +const recovered = await recoverAddress({ |
| 84 | + hash: hashMessage({ raw: messageHash }), |
| 85 | + signature, |
| 86 | +}); |
| 87 | + |
| 88 | +if (recovered.toLowerCase() !== expectedAirnode.toLowerCase()) { |
| 89 | + throw new Error('Signature verification failed'); |
| 90 | +} |
| 91 | +``` |
| 92 | + |
| 93 | +For raw responses, hash the JSON before verifying: |
| 94 | + |
| 95 | +```typescript |
| 96 | +import { toBytes, keccak256 as keccak } from 'viem'; |
| 97 | + |
| 98 | +const rawDataHash = keccak(toBytes(JSON.stringify(rawData))); |
| 99 | +// Use rawDataHash in place of `data` in the encodePacked call above |
| 100 | +``` |
| 101 | + |
| 102 | +## Step 5: Decode the data |
| 103 | + |
| 104 | +Encoded responses contain ABI-encoded values. Decode them with viem. |
| 105 | + |
| 106 | +```typescript |
| 107 | +import { decodeAbiParameters } from 'viem'; |
| 108 | + |
| 109 | +// For an int256 value (e.g., price with 18 decimals) |
| 110 | +const [value] = decodeAbiParameters([{ type: 'int256' }], data); |
| 111 | + |
| 112 | +// Convert from 18 decimals to human-readable |
| 113 | +const price = Number(value) / 1e18; |
| 114 | +console.log(`ETH price: $${price}`); // ETH price: $3842.17 |
| 115 | +``` |
| 116 | + |
| 117 | +Raw responses need no decoding. Access the JSON directly: |
| 118 | + |
| 119 | +```typescript |
| 120 | +const ethPrice = rawData.ethereum.usd; // 3842.17 |
| 121 | +``` |
| 122 | + |
| 123 | +## Step 6: Submit on-chain (optional) |
| 124 | + |
| 125 | +Pass the signed data to an on-chain verifier contract. See [On-Chain Integration](/docs/consumers/on-chain) for contract |
| 126 | +examples using AirnodeVerifier (pull) and AirnodeDataFeed (push). |
| 127 | + |
| 128 | +## Choosing encoding at request time |
| 129 | + |
| 130 | +Some endpoints leave encoding unset, letting the client choose at request time. Pass reserved parameters in the request |
| 131 | +body: |
| 132 | + |
| 133 | +```json |
| 134 | +{ |
| 135 | + "parameters": { |
| 136 | + "ids": "ethereum", |
| 137 | + "vs_currencies": "usd", |
| 138 | + "_type": "int256", |
| 139 | + "_path": "ethereum.usd", |
| 140 | + "_times": "1000000000000000000" |
| 141 | + } |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +- `_type` -- the Solidity ABI type to encode as (`int256`, `uint256`, `bool`, `bytes32`, `address`, `string`, `bytes`) |
| 146 | +- `_path` -- dot-separated JSON path to extract from the upstream response |
| 147 | +- `_times` -- multiplier applied before encoding (use `1e18` for 18 decimal precision) |
| 148 | + |
| 149 | +Both `_type` and `_path` are required together. `_times` is optional and defaults to no multiplication. |
| 150 | + |
| 151 | +## What can go wrong |
| 152 | + |
| 153 | +| Status | Error | What to do | |
| 154 | +| ------ | ------------------------------------------------ | ------------------------------------------------------------------------------- | |
| 155 | +| `400` | `Missing required parameter(s): X` | Add the missing parameters to your request body. | |
| 156 | +| `400` | `Both _type and _path are required for encoding` | You sent one reserved parameter without the other. Send both or neither. | |
| 157 | +| `401` | `Missing X-Api-Key header` | The endpoint requires authentication. Add `X-Api-Key: your-key` to the request. | |
| 158 | +| `401` | `Invalid API key` | The key value is wrong. Check with the airnode operator. | |
| 159 | +| `404` | `Endpoint not found` | The endpoint ID is incorrect. Verify the ID with the operator. | |
| 160 | +| `413` | `Request body too large` | The request body exceeds 64KB. Reduce the payload size. | |
| 161 | +| `415` | `Unsupported Media Type` | Set `Content-Type: application/json`. | |
| 162 | +| `429` | `Rate limit exceeded` | Wait and retry. The airnode has a request rate limit configured. | |
| 163 | +| `502` | `API call failed` | The upstream API is unreachable or returning errors. Try again later. | |
| 164 | +| `502` | `No value found at path: $.X` | The upstream response shape changed or the path is wrong. Contact the operator. | |
0 commit comments