SkillAgentSearch skills...

Cobalt

Standalone unofficial fully-featured Whatsapp Web and Mobile API for Java and Kotlin

Install / Use

/learn @Auties00/Cobalt

README

Cobalt

IMPORTANT: The library is currently being rewritten to prepare for a stable release. The code on the master branch is under active development.

Whatsapp4j has been renamed to Cobalt to comply with an official request coming from Whatsapp. To be clear, this library is not affiliated with Whatsapp LLC in any way. This is a personal project that I maintain in my free time

What is Cobalt

Cobalt is a library built to interact with Whatsapp. It can be used with:

  1. Whatsapp Web (Companion)
  2. Whatsapp Mobile (Personal and Business)

Donations

If you like my work, you can become a sponsor here on GitHub or tip me through:

I can also work on sponsored features and/or projects!

Java version

This library requires at least Java 21.

GraalVM native compilation is supported!

Breaking changes policy

Until the library doesn't reach release 1.0, there will be major breaking changes between each release. This is needed to finalize the design of the API. After this milestone, breaking changes will be present only in major releases.

Can this library get my device banned?

While there is no risk in using this library with your main account, keep in mind that Whatsapp has anti-spam measures for their web client. If you add a participant from a brand-new number to a group, it will most likely get you banned. If you compile the library yourself, don't run the CI on a brand-new number, or it will get banned for spamming too many requests(the CI has to test that all the library works). In short, if you use this library without a malicious intent, you will never get banned.

How to install

Maven

<dependency>
    <groupId>com.github.auties00</groupId>
    <artifactId>cobalt</artifactId>
    <version>0.0.10</version>
</dependency>

Gradle

  • Groovy DSL

    implementation 'com.github.auties00:cobalt:0.0.10'
    
  • Kotlin DSL

    implementation("com.github.auties00:cobalt:0.0.10")
    

Javadocs & Documentation

Javadocs for Cobalt are available here. The documentation for this project reaches most of the publicly available APIs(i.e. public members in exported packages), but sometimes the Javadoc may be incomplete or some methods could be absent from the project's README. If you find any of the latter, know that even small contributions are welcomed!

How to contribute

As of today, no additional configuration or artifact building is needed to edit this project. I recommend using the latest version of IntelliJ, though any other IDE should work. If you are not familiar with git, follow these short tutorials in order:

  1. Fork this project
  2. Clone the new repo
  3. Create a new branch
  4. Once you have implemented the new feature, create a new merge request

Check the frida module to understand how I go about reversing features

Disclaimer about async operations

This library heavily depends on async operations using the CompletableFuture construct. Remember to handle them as your application will terminate without doing anything if the main thread is not executing any task. Please do not open redundant issues on GitHub because of this.

How to create a connection

<details> <summary>Detailed Walkthrough</summary>

To create a new connection, start by creating a builder with the api you need:

  • Web
    Whatsapp.webBuilder()
    
  • Mobile
      Whatsapp.mobileBuilder()
    

If you want to use a custom serializer, specify it:

.serializer(new CustomControllerSerializer())

Now select the type of connection that you need:

  • Create a fresh connection
    .newConnection(someUuid)
    
  • Retrieve a connection by id if available, otherwise create a new one
    .newConnection(someUuid)
    
  • Retrieve a connection by phone number if available, otherwise create a new one
    .newConnection(phoneNumber)
    
  • Retrieve a connection by an alias if available, otherwise create a new one
    .newConnection(alias)
    
  • Retrieve a connection by id if available, otherwise returns an empty Optional
    .newOptionalConnection(someUuid)
    
  • Retrieve the first connection that was serialized if available, otherwise create a new one
    .firstConnection()
    
  • Retrieve the first connection that was serialized if available, otherwise returns an empty Optional
    .firstOptionalConnection()
    
  • Retrieve the last connection that was serialized if available, otherwise create a new one
    .lastConnection()
    
  • Retrieve the last connection that was serialized if available, otherwise returns an empty Optional
    .lastOptionalConnection()
    

You can now customize the API with these options:

  • name - The device's name for Whatsapp Web, the push name for Whatsapp's Mobile
    .name("Some Custom Name :)")
    
  • version - The version of Whatsapp to use
    .version(new Version("x.xx.xx"))
    
  • autodetectListeners - Whether listeners annotated with @RegisterListener should automatically be registered
    .autodetectListeners(true)
    
  • whatsappMessagePreviewHandler - Whether a media preview should be generated for text messages containing links
    .whatsappMessagePreviewHandler(TextPreviewSetting.ENABLED_WITH_INFERENCE)
    
  • checkPatchMacs - Whether patch macs coming from app state pulls should be validated
    .checkPatchMacs(checkPatchMacs)
    
  • proxy - The proxy to use for the socket connection
    .proxy(someProxy)
    

There are also platform specific options:

  1. Web
    • historyLength: The amount of messages to sync from the companion device
      .historyLength(WebHistoryLength.THREE_MONTHS)
      
  2. Mobile
    • device: the device you want to fake:
      .device(CompanionDevice.android(false)) // Standard Android
      .device(CompanionDevice.android(true)) //Business android
      .device(CompanionDevice.ios(false)) // Standard iOS
      .device(CompanionDevice.ios(true)) // Business iOS
      .device(CompanionDevice.kaiOs()) // Standard KaiOS
      
    • businessCategory: the category of your business account
      .businessCategory(new BusinessCategory(id, name))
      
    • businessEmail: the email of your business account
      .businessEmail("email@domanin.com")
      
    • businessWebsite: the website of your business account
      .businessWebsite("https://google.com")
      
    • businessDescription: the description of your business account
      .businessDescription("A nice description")
      
    • businessLatitude: the latitude of your business account
      .businessLatitude(37.386051)
      
    • businessLongitude: the longitude of your business account
      .businessLongitude(-122.083855)
      
    • businessAddress: the address of your business account
      .businessAddress("1600 Amphitheatre Pkwy, Mountain View")
      

IMPORTANT: All options are serialized: there is no need to specify them again when deserializing an existing sessionRecord

Finally select the registration status of your sessionRecord:

  • Creates a new registered sessionRecord: this means that the QR code was already scanned / the OTP was already sent to Whatsapp

    .registered()
    
  • Creates a new unregistered sessionRecord: this means that the QR code wasn't scanned / the OTP wasn't sent to the companion's phone via SMS/Call/OTP

    If you are using the Web API, you can either register via QR code:

    .unregistered(QrHandler.toTerminal())
    

    or with a pairing code(new feature):

    .unregistered(yourPhoneNumberWithCountryCode, PairingCodeHandler.toTerminal())
    

    Otherwise, if you are using the mobile API, you can decide if you want to receive an SMS, a call or an OTP:

    .verificationCodeMethod(VerificationCodeMethod.SMS)
    

    Then provide a supplier for that verification method:

    .verificationCodeSupplier(() -> yourAsyncOrSyncLogic())
    

    Finally, register:

    .register(yourPhoneNumberWithCountryCode)
    

Now you can connect to your sessionRecord:

.connect()

to connect to Whatsapp. Remember to handle the result using, for example, join to await the connection's result. Finally, if you want to pause the current thread until the connection is closed, use:

.awaitDisconnection()
</details> <details> <summary>Web QR Pairing Example</summary>
Whatsapp.webBuilder() // Use the Web api
      .newConnection() // Create a new connection
      .unregistered(QrHandler.toTerminal()) // Print the QR to the terminal
      .addLoggedInListener(api -> System.out.printf("Connected: %s%n", api.store().privacySettings())) // Print a message when connected
      .addDisconnectedListener(reason -> System.out.printf("Disconnected: %s%n", reason)) // Print a message when disconnected
      .addNewChatMessageListener(message -> System.out.printf("New message: %s%n", message.toJson())) // Print a message when a new chat message arrives
      .connect() // Connect to Whatsapp asynchronously
      .join() // Await the result
      .awaitDisconnection(); // Wait 
</details> <details> <sum
View on GitHub
GitHub Stars870
CategoryDevelopment
Updated2d ago
Forks248

Languages

Java

Security Score

100/100

Audited on Mar 22, 2026

No findings