How I built a tool to solve an obsession problem

Smit Thakkar
5 min readApr 22, 2024

--

Recently I applied to USCIS for my work visa. USCIS returned me a receipt with a number which I can use to track the status of my application. Since the day I received that number, I might have checked the status for at least 200 times. I would open their website, enter the number, check the status and go back to what I was doing before.

After a time I realized that I have become obsessed with checking the status, and would feel so out of control when status doesn’t change. To solve this, I built a tool that will send me status of my application over an email every day.

Now I just spend a second to look at the email in the morning and will not worry about my status updates throughout the day.

Here’s how to built it using AWS

API discovery

Most of the websites are powered by APIs in the backend. Browser loads the frontend code (html, css and javascript) which then makes API calls to backend for getting the data. In the following video, I show how to discover the backend APIs by interacting with the website.

You can copy the network request as shown below. Note that there are two requests made to USCIS backend API (https://egov.uscis.gov/csol-api) . First is to get the authorization token and second is to get case status. The second requests uses auth token from first request in the Authorization header.

The second request to get case status looks something like this.

fetch("https://egov.uscis.gov/csol-api/case-statuses/IOE0923724266", {
"headers": {
"accept": "*/*",
"accept-language": "en-US,en;q=0.9,fr;q=0.8",
"authorization": "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJVSV9VU0VSIiwiaWF0IjoxNzEzNzUwMjMxLCJleHAiOjE3MTM3NTE0OTEsInJlY2VpcHROdW1iZXIiOiJJT0UwOTIzNzI0MjY2In0.pkEuVpg2CwQDkP0g3WvThoHqSQ_OuxkY9U2EGT6Gw0U",
"content-type": "application/json",
"sec-ch-ua": "\"Google Chrome\";v=\"123\", \"Not:A-Brand\";v=\"8\", \"Chromium\";v=\"123\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"macOS\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"cookie": "_ga=GA1.3.75960515.1710115300; _ga_0KPFHBX2M9=GS1.1.1712795437.2.1.1712795463.34.0.0; _ga_WENZ0N4TZX=GS1.1.1712795467.2.1.1712795664.60.0.0; nmstat=78519331-a7dc-fb9c-93a6-ed21ed818864; _ga=GA1.1.75960515.1710115300; _ga_QY7WXHK8CT=GS1.1.1713365843.1.1.1713366571.49.0.0; _ga_3LRB05G9HS=GS1.1.1713365843.1.1.1713366571.0.0.0; TS014337b4=01e4295156b185604490286f0ace5b33e8413893d02f9012e1a021a108e8cc5b0a13adc8d5ea09ff37566ccfd35e027bbcecd52ad6; _gid=GA1.3.608295938.1713750211; _gat_GSA_ENOR0=1; _ga_CSLL4ZEK4L=GS1.1.1713750210.6.1.1713750220.0.0.0; _ga_PKV493T59N=GS1.1.1713750210.6.1.1713750230.40.0.0",
"Referer": "https://egov.uscis.gov/",
"Referrer-Policy": "strict-origin-when-cross-origin"
},
"body": null,
"method": "GET"
});

Integration with API using Javascript

I chose Javascript language because it’s easiest to work with in web development. Below is the code snippet to integrate with API in Javascript. I removed few headers from the request because of their irrelevance.

const authResponse = await fetch("https://egov.uscis.gov/csol-api/ui-auth", {
"headers": {
"accept": "*/*",
"accept-language": "en-US,en;q=0.9,fr;q=0.8",
"content-type": "application/json"
},
"body": null,
"method": "GET",
}).then(response => response.json());

const token = authResponse.JwtResponse.accessToken;
const authHeader = `Bearer ${token}`

const response = await fetch("https://egov.uscis.gov/csol-api/case-statuses/IOE0923724266", {
"headers": {
"accept": "*/*",
"accept-language": "en-US,en;q=0.9,fr;q=0.8",
"authorization": authHeader,
"content-type": "application/json"
},
"body": null,
"method": "GET"
}).then(response => response.json());

console.log("received response from USCIS", response);

const statusText = response.CaseStatusResponse.detailsEng.actionCodeText;
const statusDesc = response.CaseStatusResponse.detailsEng.actionCodeDesc;

Running code on AWS Lambda

This part requires an account on AWS. Once you setup your account, go to Lambda and create a function. Choose the Node.js 20.x as runtime. This will create a default function. Add the above code snippet in the handler function. The Lambda code will look like this

export const handler = async (event) => {

try {
// above code snippet

const statusText = response.CaseStatusResponse.detailsEng.actionCodeText;
const statusDesc = response.CaseStatusResponse.detailsEng.actionCodeDesc;

// return success code
return {
statusCode: 200,
body: JSON.stringify('Status fetched successfully')
};
} catch (error) {
// return failure code
return {
statusCode: 500,
body: JSON.stringify('Failed to fetch status')
};
}

Send an email

We can now send the statusText and StatusDesc as an email. For this, we will integrate with SNS. It’s a Notification service. You can create a topic and people can subscribe to that topic via email or mobile. Any notification published to that topic will be sent to subscriber via SNS service.

Go to AWS SNS and create a topic. The topic name I kept is UscisCaseUpdate . Subscribe to that topic by providing your email address.

The code snippet for Lambda to SNS integration looks like this.

import { SNSClient } from '@aws-sdk/client-sns';

const accountId = "273683019430";
const region = "us-west-2";
const snsTopic = "UscisCaseUpdate";

// Create an SNS object
const snsClient = new SNSClient({ region: region });
import { PublishCommand } from '@aws-sdk/client-sns';

export const handler = async (event) => {

try {
// above code snippet

const statusText = response.CaseStatusResponse.detailsEng.actionCodeText;
const statusDesc = response.CaseStatusResponse.detailsEng.actionCodeDesc;

// Define the parameters for sending a message to the SNS topic
const params = {
TopicArn: `arn:aws:sns:${region}:${accountId}:${snsTopic}`,
Message: `Status: ${statusText}\nDescription: ${statusDesc}`
};

// Publish the message to the SNS topic
const command = new PublishCommand(params);
const data = await snsClient.send(command);
console.log('Message published successfully:', data);

// return success code
} catch (error) {
// return failure code
}

Set periodic invocation of Lambda

The last part is to invoke our Labmda periodically so that it can fetch and send emails. We will use AWS EventBridge service for this. Go to AWS EventBridge and create a scheduler. Choose the above Lambda function as the target and set 00 06,14 * * ? * as cron expression. Following is the meaning of this expression.

It will invoke Lambda daily two times, first at 6:00 AM and second at 2:00 PM of the selected timezone.

This is how the overall architecture look like.

Thoughts

It felt powerful to build something like this to solve my own problem. I like the idea of making own’s life a little better by building or using tools like this.

If you also like to solve your own problems by building something cool, follow me on Twitter and LinkedIn for more ideas and resources like this.

Clap, follow, and let’s connect!

Edit

Finally I saw the email that I was waiting for. My EAD card was produced today. 🙂

--

--

Smit Thakkar
Smit Thakkar

Written by Smit Thakkar

Software Developer at DoorDash. Passionate about learning, sharing, building products and solving problems

Responses (2)